On LanguagePissingMatch, WilliamGrosso wrote:
The second comment is about late-binding. It's not entirely obvious to me that Java and Smalltalk have the same notion of "late binding."
The model in Java is as follows: during compilation (generation of byte code), references to other object methods are generated. These references are symbolic and include a complete function signature.
So, consider something like
void functionFoo(Bar argument) { x.methodName(argument); }What method gets called on x ? Well, the signature will be resolved, at compile time, using the information known to the compiler (e.g. relying on the declared types of argument and x, rather than the types of run-time instances). During run-time, this signature will be matched against the actual class of the instance referred to by x. But, for example, if this class had both
methodName(Bar argument) methodName(subclassOfBar argument)and argument actually was an instance of subclassOfBar, the first method would still be called.
Which is not what I would think of as "late binding."
What would Smalltalk do in an analogous situation (e.g. which option do Smalltalk programmers find intuitive) ?
Smalltalk, like Eiffel, does not allow code like that at all. A class can only have one method called "methodName". In Smalltalk you cannot declare the type of expected arguments - it is not a manifestly-typed language.
Yes, I know. That's why I asked what Smalltalk programmers would find intuitive, rather than what Smalltalk does. I was an ObjectiveCee programmer for 4 years before moving to Java and my intuition was very much for late-binding in the above sense, rather than what Java does.
This is slightly confused because Smalltalk has a variant of keyword arguments. You can have methods:
self methodWith: arg1. self methodWith: arg1 and: arg2.and these are actually different methods with names "methodWith:" and "methodWith:and:", rather than a single "methodWith" overloaded twice.
What you seem to be asking here is full dynamic dispatch on all arguments. This is sometimes called MultiMethods. Smalltalk, C++, Eiffel and Java give you dynamic dispatch only on the first argument (which becomes the hidden "self" or "this" argument). MultiMethods is an obvious generalisation and there are patterns to simulate it (DoubleDispatch, VisitorPattern). There are languages which support MultiMethods directly, eg the DylanLanguage, CommonLisp, the CecilLanguage, and they are very interesting, but they tend to screw up the encapsulation. You lose the notion that methods "belong" to a given class - they arguably belong to every class they dispatch upon. -- DaveHarris
I didn't follow the last paragraph. What I was talking about is dispatch that ignores the sender argument and instead binds the method call based only on the actual (run-time) type of this. I don't have DP here, but I don't recall either DoubleDispatch or VisitorPattern behaving like this.
I think I see what is confusing you. Java does in fact have late binding which can operate exactly in the manner as Smalltalk - that is, singly-dispatched polymorphic methods. Java also incorporates the concept of overloading on argument types to methods; this is a superset of Smalltalk's method binding functionality. Java's overloaded methods allows a multi-method style of programming.
Condider:
class Test { void foo(Vector v) {...} void foo(Stack s) {...} } Test t = new Test(); t.foo(aVector); t.foo(aStack);The first call to foo() binds to the the first method, the second call binds to the second method. (Note that Stack is a subclass of Vector.)
Of course, if "t" were an instance of some other class, foo() would be bound according to the methods implemented in that class, not in the Test class.
Personally, I find this behavior understandable, predictable, and convenient. The problem I have with CLOS-style GenericFunctions is that they are none of those things. In Java, I can predict what method "super()" will bind to. In CLOS, predicting what "call-next-method" does is right up there with predicting the lottery numbers. As for encapsulation, I don't think Java-style overloading breaks it. The method is always IN a particular class. I just think of it as syntactic sugar for DoubleDispatch.
Oh, I thought he understood that (He did). He wrote: "During run-time, this signature will be matched against the actual class of the instance referred to by x". If all dispatching were done statically, Java would be a much less useful language.
The CommonLispObjectSystem is more complex for other reasons. For example, super() doesn't make sense if you have multiple inheritance, as in C++, regardless of whether you also have multiple dispatch. There is more to method combinators than multiple dispatch.
The static dispatch can be confusing in Java. In "x.method(y)", it uses not just the static type of 'y' but also only those methods declared in the static type of 'x'. And if new methods are added to that class but the call site isn't recompiled, they won't be seen. I certainly sympathise with those who don't like it. I think the static multiple dispatch should be enhanced to dynamic multiple dispatch rather than reduced to (dynamic) single dispatch, but I realise that doing this well is an open research question. -- DaveHarris
The above is before march 2001 below is feb 2002
void functionFoo(Bar argument) { x.methodName(argument); }In SmallTalk, you do:
functionFoo: argument argument methodName: xLikewise in Eiffel. I call this ArgumentInversion?
The Java example that JoshuaSusser gives above isn't late binding, unless late binding doesn't mean what I think it means. The method selection in that example is done at compile time. When the compiler sees "t.foo(aVector);" it needs to know whether aVector is a Vector or a Stack. (The actual method name in the .class file includes all the argument types and the return type.) To switch on a type not known at runtime, you'd have to explicitly do "instanceof" and cast it to the appropriate type before the method call. --SteveDunham