Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: If OOP is about message passing, why not sender.send(receiver, message)?
29 points by revskill 51 days ago | hide | past | favorite | 38 comments
I learnt that (from Alan Kay), OOP is about message passing, but in most of OOP supported language, the syntax is always


While i think it should be

sender.message(receiver, params)


sender.send(receiver, message, params)

Am i missing anything ?

I would argue (contra Kay) that OOP really isn't about message passing; an object is fundamentally a state machine, not an actor or a fiber. So it should really be:

thing_undergoing_state_change .state_change(params)

This is an obligatory comment that you feel this way because of popular languages that have the OOP label that don't have much of any resemblance to what Kay intended as OOP.

To understand his vision more, look at Smalltalk. Messages are more than just re-branded procedure calls, they can be inspected and forwarded to other objects (i.e. delegation) for example.

I think you are exactly correct. The success of OOP thinking is actually one of the things I don't like sometimes; we are so focused on creating state machines with carefully crafted interfaces that we forget that we can have stateless functions as part of our state machines. In general, I wish we were more careful with only creating these state machines when the alternative is more complex, or when the problem domain is easily modeled by one.

Yes. You miss polymorphism.

The reason for receiver.method(), is that the binding to the receiver is done in runtime and not in compile time. Hence, you can change the ACTUAL receiver object without changing the sender.

    While i think it should be

    sender.message(receiver, params)
Why do you think that? Not trolling - it's hard to understand what you might or might not be "missing" without understanding your thought process.

In the overwhelming majority of cases, "sender" is implied by context; objects do not send messages on behalf of other objects.

You might want to look at Barbara Liskov’s work on object oriented programming. It refined a lot of those concepts, and looks closer to modern OOP than Kay’s sender model.



> I learnt that (from Alan Kay), OOP is about message passing

I just like to point out that Alan Kay has a very particular idea of OOP that doesn't match the current mainstream usage. None of the modern OOP languages are “object oriented“ in Alan Kay's meaning of the term.

One sort-of exception is Erlang (and anything else that runs on the BEAM vm, like Elixir). It doesn't match Alan Kay's vision exactly, because the message passing is done a bit differently (for example it's asynchronous), but creator Joe Armstrong called it something like "the best of functional programming and OOP".

If you come from C++, Java, or other "OOP" languages, and look at Erlang, it's completely different than what you expect. In Erlang, the "objects" are less like mutable structs and more like functional mini-programs that can create new programs, pass messages, and fail as you would expect.

To get some perspective on both of their perspectives, here's "Joe Armstrong interviews Alan Kay":


> for example it’s asynchronous

Ruby also lets you build programs like this as of 3.0, using Ractor#send / Ractor::receive https://docs.ruby-lang.org/en/master/doc/ractor_md.html#labe...

It’s still pretty new so a lot of the Gem ecosystem hasn’t caught up yet (e.g. C extensions need to be explicitly opted-in as Ractor-safe), but I built a new “MIME::Types” library replacement with it recently and have enjoyed very few teething issues: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...

> Joe Armstrong called it something like "the best of functional programming and OOP".

Though IIUC Armstrong only started doing that after others pointed out that it was OOP.

I think Alan Kay's OOP is widely used, but not at the "program" level. From what I understand, according to Alan Kay, OOP is about message passing, late binding and internal state. This is the way lots of software works these days with JSON over HTTP, or protocol buffers, or something else. I think at some point people imagined that there would be a point where you could buy or use objects from other people to build your programs more efficiently. I think that's what happened, with some companies offering very specific objects (auth for example, or persistance), while other offer a library with everything you need (cloud providers). And we even "rediscovered" the concept of interface in a way with stuff like "compatible with the S3 API".

Imagine that you have three friends helping you with a task. You might give them these instructions:

    Jack, clean the shelves.
    Helen, sort the reports.
    Lucy, turn on the printer.
You can see that the receiver of each message is the first thing in each line while the message follows it. That is the syntax of imperative expressions in English.

If your name is Kevin and you are the one sending on the messages, where is "Kevin" in the above text? And you sent three messages and yet there is not a single "send" command to be seen.

Here is a less trivial example:

     Jack hire a carpenter build cabinet store reports.
This is not valid English, but is a valid Smalltalk-72 expression. A variation which would have the same result is:

     ((Jack hire a carpenter) build cabinet) store reports.
The first message requests that Jack hire a carpenter. The result of the message is a carpenter who is not named by us, but which we can tell to build a cabinet. The result of this second message is a new cabinet, which we did not name either but which can be asked to store the reports.

So Smalltalk got its syntax from English and that inspired OOP languages that followed.

> If your name is Kevin and you are the one sending on the messages, where is “Kevin” in the above text?

It’s not, but if it was radio calls:

  Jack, this is Kevin, clean the shelves, over.
  Kevin, this is Jack, wilco, out.
Which is analogous to asynchronous message passing on a shared bus; but even then its still (in programming terms) like (using hypothetical syntax):

With typical in-band request-response message sending, there is no need to specify the sender, as a return goes back to the sender implicitly. (And programming platforms that do use asynchronous message passing typically don’t make the sender explicitly identify itself, though the receiver does typically get a handle for the sender to respond to, so it looks something like the above from the receiver side, if not the sender side.)

Great question! I've thought about this too, in my younger days.

All of this is IMHO:

* an abstract concept like Alan Kay's OOP is not the same as programming language syntax * your examples are programming language specific. Python, C++, Java-ish, from the looks of it * Alan Kay's OOP, as far as I understand it, is about /disconnecting/ the sender and receiver, and have them "communicate" via messages. * the difference between `receiver.message(params)`, `sender.message(receiver, params)`, or `sender.send(receiver, message, params)` is skin-deep. You can easily write a library that converts one to the other in almost any of the languages.

You're taking a complicated subject and reducing it to talking about what amounts to be pretty superficial syntax. There's more to messaging than just how the syntax of sending a message - how do you represent messages? Call by value or reference? Is the call synchronous or asynchronous (e.g. Erlang)?

Also, in terms of syntax, why not:

    send(receiver, sender, message)
Why is the act of sending tied to the subjects at all, and not the responsibility of the system itself?

Kay’s favorite OOP language was Smalltalk:

    receiver message:params

    condition ifTrue:[dog bark]
There isn’t an explicit sender here.

Order of arguments is just an irrelevant syntactic detail as long as only the receiver chooses the method implementation.

There is an alternative approach called multiple dispatch or multimethods where every function argument can be used to look up the appropriate method to call. It’s implemented in e.g. Julia.

Objects are records with a controlled interface, so that you don't have to see the internal details. This allows restructuring of the internals without disrupting the code that uses the objects. It is a fairly well understood mechanism for preventing unwanted side effects.

The interface is type checked, and the appropriate way set the type rules is to bundle them with the interface (and thus the object), not with the user code.

> Am i missing anything ?

> sender.message(receiver, params)

It's the receiver that determines what messages they can understand, and the sender itself rarely has anything to do with the message apart from sending it. It doesn't make sense to make the sender an important part of the syntax for message-sending. By keeping the sender out of the syntax, the syntax of a particular call is the same no matter how the sender changes from moving the code around.

Also, writing it as receiver.message(params) follows English grammar of subject-verb-object ordering, allowing the code to read more naturally.

> sender.send(receiver, message, params)

If you're asking about the use of the word "send", at least Ruby uses it, but it's receiver.send(message, params). That particular call does not follow English grammar, but you only ever use the "send" message when the message is variable. It's not common.

It could even be expressed as `(params message receiver)`, or `message; <params>.{{receiver}}` or whatever other syntax.

But it's going to be more thoughtful to explain what parts of programming or of software design or architecture are emphasised by different ways of arranging things.

`receiver.message(params)` puts an emphasis that it's the receiver which receives and acts upon the message. The receiver will conform to some kind of interface with which we can interact.

`sender.message(...)` seems completely bizarre to me. -- Some module's interaction with other things is where the interactions are defined? Sounds interesting if it works!

Hypothetically, you could design a language where methods aren't defined by the thing before the dot, but by the last argument, I guess? This doesn't seem like a very sensible thing to do, though.

I think you are right in thinking it must be sender based. But it might not sit well with dev ergonomics. But almost every actor based implementations do what you are suggesting in one form or the other

The ‘sender’ is the code doing the call and the context. Most languages don’t let a method know who called it, so it is not a ‘full fat’ implementation of messaging which would be more like email.

The Quicksilver word processor / desktop publishing system offered a user programming interface in lisp.

The Quicksilver lisp used object-oriented programming and most of the documents and desktop system were objects. Calling methods on those objects originally used a function called "tell" for message passing:

(tell object method parameter1 parameter2)

Later they deprecated "tell" in favor of:

(method object parameter1 parameter2)

I think usually the sender is implicit (the calling context) so it could me omitted.

Racket [1] has a non conventional syntax too.

[1] https://docs.racket-lang.org/reference/ivaraccess.html#%28fo...

Learn Smalltalk.

  myObj 4 "this sends the number four to myObj. Yes, comments are in quotation marks"
  4 factorial "yep, numbers are objects too"
  0 + 4 "sends the 4 as a message to 0"
  myObj arg1: 1 arg2: 'a' "if you need more than 2 arguments you use keyword messages"

This is effectively how all calls in Objective-C work. Everything turns into

objc_msgSend(receiver, message, params)

> Am i missing anything ?

Conventionally, the send is always implemented in the sender, so explicit sender makes no sense.

This is true even in Erlang as opposed to class based OOP, though there the syntax is:

  receiver ! message

> receiver.message(params)

> sender.message(receiver, params)

The only difference here is that sender is implicit. Since you are writing code in an object's context, the sender is this object (a keyword in many languages).

You're missing nothing. Class based OOP is essentially a set of arbitrary ontologies forced upon you by library authors. Your interpretation is no more or less meaningful than the others.

Within the execution environment the sender is known (it’s the current object) and the verb “send” is generic and thus not informative so typing sender.send over and over is unnecessary

FYI, the signal-slot paradigm has been introduced and popularized by Qt over 20 years ago. https://en.wikipedia.org/wiki/Signals_and_slots

sender.send(receiver, message, params)

is really sending a message to sender with content {send, receiver, message, params}

If you want the receiver to know who the sender is, you need to pass that along in the message. Personally, I find that's easier to manage if it's explicit rather than implicit. If I want to send a message and have replies go to me, I'd say the sender is self or this or what have you; but if replies should go elsewhere, I can easily do that. With an implicit sender, you have to route the sending through the sender to get the reply to the right place and it adds considerable structual complexity.

I don't know the actual answer, but that would be a lot of useless typing.

Well, at least that was how Smalltalk (which is what Kay does contribute to) works. And if you have actors, it also looks similar as well (when you pass a message to a mailbox).

> sender.send(receiver, message, params)

A lot of very bad corporate software has informal object-oriented-programming implementations that either look like this, or map to it directly.

Look to the inventors of Object Orientation to learn what it is about. They won a Turing award for it after all. Hint: It wasn't Alan Kay.

Simula I was a huge influence on Alan, as was Sketchpad (a parallel invention of object oriented programming). But Simula was a hybrid language like the later C++, so in the issue about message passing syntax Smalltalk had a certain impact.

Simula-67 introduced inheritance, which Smalltalk-72 and -74 didn't have. Many people see it as a key OOP feature, which Smalltalk-76 added.

Kristen Nygaard, who with Ole-Johan Dahl had created Simula, was one of the creators of the very pure Beta programming language.


This took the message passing syntax in a very different direction:

    message -> receiver

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact