Hacker News new | ask | show | jobs
by revvx 2615 days ago
> Isn't a method call a message, and the return value a message back?

It is!

In my view, the point that Alan Kay and Joe Armstrong are trying to make is that languages like C++/Java/C# etc have very limited message passing abilities.

Alan Kay uses the term "late binding". In Kay's opinion, "extreme late binding" is one of the most important aspects of his OOP [1], even more important than polymorphism. Extreme late binding basically means letting the object decide what it's gonna do with a message.

This is what languages like Objective-C and Ruby do: deciding what to do after a method is dispatched always happen during runtime. You can send a message that does not exist and have the class answer to it (method_missing in Ruby); you can send a message to an invalid object and it will respond with nil (Objective-C, IIRC); you can delegate everything but some messages to a third object; you can even send a message to a class running in other computer (CORBA, DCOM).

In C++, for example, the only kind of late binding that you have is abstract classes and vtables.

-

> Or is it that "true OO" must be asynchronous?

It doesn't have to be asynchronous, but in Alan Kay's world, the asynchronous part of messaging part should be handled by that "dispatcher", rather than putting extra code in the sender or the receiver.

I don't remember Alan Kay elaborating on it, but he discusses a bit about this "interstitial" part of OOP systems in [2]

-

[1] - https://en.wikipedia.org/wiki/Late_binding

[2] - http://wiki.c2.com/?AlanKayOnMessaging

6 comments

C++'s vtable is also late binding, since you don't know which implementation you're calling until runtime. And there's no such thing as "extremely late binding".

> In C++, for example, the only kind of late binding that you have is abstract classes and vtables.

That's not true, you can always have a "send_message(string id)". Few people do it because you lose static type safety. And some languages, like C# and Scala, have dynamic types that allows for the "method_missing" protocol and such features are very unpopular.

To be honest I don't see much of a difference. I've worked with a lot of dynamic OOP languages, including with Erlang-style actors and I've never seen the enlightenment of dynamic OOP message passing.

And I actually like OOP, but I don't really see the point of all this hyperbole about Smalltalk.

> That's not true, you can always have a "send_message(string id)". Few people do it because you lose static type safety. And some languages, like C# and Scala, have dynamic types that allows for the "method_missing" protocol and such features are very unpopular.

That is the difference. If every class in C++ had only one method - send_message and each object is an independent thread, you will get how Erlang works. That is how you would do the actor model in C++.

Inheritance, Polymorphism is emphasised in Java, C++ and C#, whereas Functional programmers emphasise function objects / lambdas / Command Pattern where you just have one method - calling the function. Infact having just method you no longer need Polymorphism / Interfaces.

What? This has nothing to do with functional programming.

FP needs polymorphism too and as a matter of fact FP tends to be even more static.

In FP we have type classes, built via OOP in static OOP languages.

> Infact having just method you no longer need Polymorphism / Interfaces.

That’s false.

It's not. You can use multiple dispatch.
But then why would you? Isn't ditching type safety a bad idea most of the time ?
> C++'s vtable is also late binding, since you don't know which implementation you're calling until runtime. And there's no such thing as "extremely late binding".

C++'s vtables are determined at compile time. The specific implementation executed at a given moment may not be possible to deduce statically, but the set of possible methods is statically determined for every call site: It consists of the set of overridden implementations of the method with that name in the class hierarchy from the named type and downwards.

No such restriction exists in Ruby or Smalltalk or most other truly dynamic languages. E.g. for many Ruby ORM's the methods that will exist on a given object representing a table will not be known until you have connected to the database and read the database schema from it, and at the same time I can construct the message I send to the object dynamically at runtime.

Furthermore the set of messages a given object will handle, or which code will handle it can change from one invocation to the next. E.g. memoization of computation in Ruby could look sort-of like this:

    class Memo
      def method_missing op
         result = ... execute expensive operation here ...
         define_singleton_method(op) { return result }
      end
    end
After the first calculation of a given operation, instead of hitting method_missing, it just finds a newly created method returning the result.

"Extreme late binding" is used exactly because people think things like vtables represent late-binding, but the ability to dynamically construct and modify classes and methods at runtime represents substantially later binding.

E.g. there's no reason why all the code needs to be loaded before it is needed, and methods constructed at that time And incidentally this is not about vtables or not vtables - they are an implementation detail. Prof. Michael Franz paper on Protocol Extension [1] provided a very simple mechanism for Oberon that translates nicely to vtables by dynamically augmenting them as code is loaded at runtime. For my (very much incomplete) Ruby compiler, I use almost the same approach to create vtables for Ruby classes that are dynamically updated by propagating the changes downwards until it reaches a point where the vtable slot is occupied by a different pointer than the one I'm replacing (indicating the original method has been overridden). Extending the vtables at runtime (as opposed to adding extra pointers) would add a bit of hassle, but is also not hard.

The point being that this is about language semantics in terms of whether or not the languages allows changing the binding at runtime, not about the specific method used to implement the method lookups semantics of each language - you can implement Ruby semantics with vtables, and C++ semantics by a dictionary lookup. That's not the part that makes the difference (well, it affects performance)

> That's not true, you can always have a "send_message(string id)". Few people do it because you lose static type safety. And some languages, like C# and Scala, have dynamic types that allows for the "method_missing" protocol and such features are very unpopular.

If you're working in a language with static typing you've already bought into a specific model; it's totally unsurprising that people who have rejected dynamic typing their language choice will reject features of their statically typed language that does dynamic typing. I don't think that says anything particularly valuable about how useful it is. Only that it is generally a poor fit for those types of languages.

[1] Protocol Extension: A Technique for Structuring Large Extensible Software Systems, ETH Technical Report (1994) http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42....

The only good thing about OO as a architecture is, that there is nearly no education required to introduce it to the most novice in the field. Its basically the default thinking approach rebranded. It comes with all the benefits of a mental model - quick orientation, and all the negative of a mental model. (Badly adapted to fit to machine execution, after acertain complexity level is reached - god like actorobjects - basically programmers in softwaredisguise- start to appear).
Disagree. The original design patterns book was really about ways oop should be used that don't fit people's everyday conception of objects. (Of course that causes different problems for the novice keen to use the patterns but that's another story)
I'm surprised the actor model hasn't been mentioned. Isn't this the modern name for what theyre talking about?

Completely independent objects passing messages and entirely parallelizable.

My first exposure to the actor model was with Akka on Scala. After working with it for a little while, I thought "this is what OOP should be, perhaps I just hate broken implementations of OOP (i.e., Java, C++), rather than OOP itself." Heck, I like Ada95's implementation of OOP better than Java's.

I keep meaning to give Erlang a try, but just haven't had a reason yet. I do a lot of Clojure, these days :)

If you like Akka/Scala, definitely give Erlang a try.
I can highly recommend Elixir as a pleasant entry point. I've been looking into learning Erlang too though, much as the syntax is a bit daunting.
what does late binding by you? That sounds like an argument for non-strictly typed languages. Isn't in the strict typing that prevents late binding? The compiler wants to know at compile time the types of all the messages and whether or not an object can handle that message hence all messages must be typed and all object must declare which messages they accept.
> what does late binding by you?

Some things that come to mind:

- Abstract classes/methods, and interfaces. This is implemented using vtables in C++.

- Ability to send messages asynchronously, or to other computers, without exposing the details of such things. You just call a method in another class and let your dispatcher handle it. There was a whole industry built around this concept in the 90s: CORBA, DCOM, SOAP. And Erlang, of course, in a different way.

- Ability to change the class/object during runtime. Like you can with Javascript and Lua, calling `object.method = `. Javascript was inspired by Self (a dialect of Smalltalk), so there's that lineage. Other languages like Python and Ruby allow it too.

- Ability to use the message passing mechanism to capture messages and answer them. Similar to Ruby's "method_missing" and ES6 Proxies in Javascript. This is super useful for DSLs and a great abstraction to work with. Check this out: http://npmjs.com/package/domz

Remember that you can have some of those things without dynamic typing (Objective-C).

Objective c is a compiled language
Objective C is compiled, yes, but like Smalltalk OOP, the target object of the message is resolved and interpreted by that object at runtime.
Objective-C is much closer to true object orientation than C++, but IMO Apple neutered it by having the program crash if there was no message handler.
It crashes only if you let it.

a) The crash is from the default unhandled exception handler, which will send a signal to abort. So if you just want to crash, you can either handle that particular exception or install a different unhandled exception handler

b) An object gets sent the -forwardInvocation: message when objc_msgSend() encounters a message the object does not understand. The exception above gets raised by the default implementation of -forwardInvocation: in NSObject.

    o := NSObject new.
    o class
    -> NSObject
    n := NSInvocation invocationWithTarget:o andSelector: #class
    n resultOfInvoking class 
    -> NSObject
    o forwardInvocation:n 
    2019-04-22 07:49:12.339 stsh[5994:785157] exception sending message: -[NSObject class]: unrecognized selector sent to instance 0x7ff853d023c0 offset: {
(This shows that -forwardInvocation: in NSObject will raise that exception, even if the NSInvocation is for a message the object understands)

If you override -forwardInvocation:, you can handle the message yourself. In fact, that is the last-ditch effort by the runtime. You will first be given the chance to provide another object to send the message to ( - (id)forwardingTargetForSelector:(SEL)aSelector; ) or to resolve the message in some other way, for example by installing the method ( + (BOOL)resolveInstanceMethod:(SEL)sel; )[0].

Cocoa's undo system is implemented this way[1], as is Higher Order Messaging[2][3]

[0] https://developer.apple.com/documentation/objectivec/nsobjec...

[1] https://developer.apple.com/documentation/foundation/nsundom...

[2] https://en.wikipedia.org/wiki/Higher_order_message

[3] https://github.com/mpw/HOM/blob/master/HOM.m

Back when I wrote a lot of obj-c is when I really 'got' message passing vs. a function call. I miss obj-c, but everyone wants to move on to Swift.
Wasn't the design decision (and implementation) involved in place log before Apple had anything to do with it?
NextStep adopted it but did not invent it. Once Apple acquired NextStep and released OS X they were the only major company supporting it and had defacto control over the language.

The complaint I have is with NSObject which can be blamed on Next Step. Although another comment pointed out I just didn’t know about a workaround.

There were two different major mutually-incompatible “flavors” of Objective-C (my first book on Objective-C covered both, and my first Objective-C programming was done on a NeXTcube), one of which originated at NeXT (NextStep was the OS that was NeXTs last major surviving product after they dropped hardware, not the company.)
Extreme Late binding: for "The Pure Function Pipeline data Flow", attaching data or metadata to the data flow, then the pipeline function parses it at run time, which is simpler, more reliable, and clearer.
C++, Java etc. all lack proper union types with appropriate pattern matching. So a lot of useful message passing patterns cannot be implemented without too much boilerplate.