Darren New wrote: > Christopher Smith wrote: >> On the contrary. It is a common Smalltalk idiom to implement decorators >> as descendants from "nil" (not UndefinedObject, but rather an instance >> of UndefinedObject) that implement doesNotUnderstand:. > > OK. I never saw that. > > Note, however, that if your decorator is a descendent of nil, and nil > is a descendent of Object, you're still a type of object. :-) As I tried to be really, really clear about: 'nil' isn't a class, so it can't be a descendant of Object or any other class. >>> That's why I said it was bounded. Unlike C++, where you can have two >>> classes who share *no* common ancestor. >> Yes, but you can't do polymorphism between them. We're talking about >> "unbounded polymorphism", not "unbounded class hierarchy". When >> Smalltalk does it's polymorphic dispatch, its only requirement is that >> the receiver be an object (which is different from a subclass of Object, >> as odd as that may seem), and since everything is an object, it doesn't >> even have to check that. > > Right. So wouldn't that make smalltalk "unbounded"? Yes, glad to see you are coming around on this. >> When you use a vtable in C++, there is no way >> to say, "invoke member foo() on whatever type this object is an instance >> of". It is always "invoke whatever is in the vtable for Bar::foo() for >> this object". > > Yes. That's what makes C++ bounded, yes? Yes, exactly. > Maybe it's just that, not having read the paper, I don't understand > the use of the technical terms. It's a book, not a paper, btw. I'll take the hit for doing a lousy job ahead of time. I'll try again to explain it (and really, I'm not sure how important it is to explain it):
1) static vs. dynamic polymorphism is about whether the polymorphism is resolved at runtime or not. A statically polymorphic system would select which method to invoke at compile time, while a dynamic one wouldn't decide until the method was actually being invoked. 2) bounded vs. unbounded is about whether the programmer has to "bind" the polymorphism to a specific base type. So, C++ templates are static and unbounded. Smalltalk message dispatch is dynamic and unbounded. C++ virtual function dispatch is dynamic and bounded. I can't think of one off the top of my head that is static and bounded, but someone probably has a generics mechanism that works that way. One way to think of it is how much work is being left to do at runtime. A dynamic unbounded system has no idea what type it is working with or what method it is invoking, so it has a lot of work to do at runtime. A dynamic bounded system knows which base class it is working with (in the case of C++ vtables, it knows which "slot" it needs to call), so the only needs to jump to the specific version of the method to invoke. A static system already knows exactly what it needs to invoke, so there isn't any runtime overhead. >>>> The only "rule" being violated, AFAIK, is casting between pointers to >>>> data and pointers to functions. >>> Right. Well, ok, now put it on an architecture where data is in one >>> address space and code in another. Suddenly it's not working again. >>> And so on. >> Not at all. I'd be highly surprised if said architecture had a fully >> POSIX compliant dlsym(). So, instead you'd use whatever is the >> appropriate equivalent for said architecture and all is well again. > > Right. And whatever the equivalent is? DllImport, IIRC. I can't remember what it's prototype looks like, which is the key thing. > You wouldn't be able to write it in C. That's all I'm saying. > Basically, if you're programming in C, there's no guarantee you're > able to do that at all. The OS would have to implement dlsym() in a > language other than C. (Of course, for something like Ada, the > compiler would be generating non-Ada code (i.e., machine language) to > implement the same thing, so it's really not that different.) Woa... hold on there. dlsym() can easily be imported in whatever language you want. The OS is dealing directly with the memory addressing/segmentation, so it can control what is gong on. The problem is what the caller can do with it. Technically, on a POSIX system, you have to cast from a void* to a function pointer, and C doesn't guarantee that you can do that. However, on a system that did keep data and code separate, I would imagine that instead of having dlsym(), you'd have some other function, say a dlsymfunction() which returned back a function pointer, and then there'd be no problem. > Which may be better or worse than other languages, where (say) there > might be a facility to do something, and the hardware/OS provides a > different way to do that which is incompatible but would be sufficient > for your purposes, and you find the language doesn't allow you to use > that other facility because it's incompatible with the language. :) You'd have to admit, it'd be a weird implementation choice that would actually cause the platform and the compiler for said platform to be incompatible with each other.... You'd think the compiler, in particular, would prove unpopular. > Pardon the rant. It's just one of those things like people saying > "these languages are both Turing complete, therefore they're equally > powerful", that when you actually understand what's going on with > Turing Completeness, you realize it's (a) a factually incorrect > statement, and (b) to the extent it's correct, it's proving the > opposite of what the speaker thinks it does. Turing Complete systems are physically impossible, so when people call a "system" Turing Complete, the notion is that given infinite resources it would be. However, a Turing Complete language... well, it is somewhat hard for a language NOT to be Turing Complete (certainly doable if you try, but C++ templates are a great example of a language that accidentally ended up being Turing Complete). --Chris -- [email protected] http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg
