
A world of message-oriented programming languages (2018) - bibyte
http://boston.conman.org/2018/11/21.1
======
theamk
I've read CALLG/CALLS VAX example, and became curious -- why waste microcode
for two types of function calls, isn't that hard? Isn't "initalizing a
temporary structure" expensive? I found the nice explanation [0] and
apparently no, it was pretty simple.

\- A parameter list is a series of words in adjacent memory cells. The AP
register points to the first of these. The first word holds a COUNT of the
number of parameters. The remaining words hold the individual parameters.

\- CALLG copies passed address to AP register. It must contain parameter list
in the right format.

\- CALLS simply pushes the parameter (argument count) to the stack. Since the
stack grows toward low addresses, the stack ends up with exactly right
sequence of words to represent a parameter list! So the instruction just
copies stack pointer to AP, and calls the function. It also sets a special
flag to pop values from stack on return.

This is a very neat approach -- with a bit more than a extra push, VAX got a
nice instruction to optimize common case of function calls.

Unfortunately, this approach would not work with modern CPUs. In modern PCs,
memory accesses are much slower that register accesses, so first few
parameters are passed in registers. Calling function with multiple parameters
is very different from calling it with one struct.

[0] [http://www.math-
cs.gordon.edu/courses/cs222/lectures/procedu...](http://www.math-
cs.gordon.edu/courses/cs222/lectures/procedures.html)

~~~
ridiculous_fish
VAX was designed in a time where it was common to write assembly by hand, and
so programmer ergonomics was an important consideration. Instructions were
introduced where today we would add a library function - this peaked with POLY
[1] I think?

Nowadays ISAs are compiler targets and nothing more, so human-ergonomic
instructions are hilariously quaint.

1:
[http://uranium.vaxpower.org/~isildur/vax/week.html](http://uranium.vaxpower.org/~isildur/vax/week.html)

------
pmontra
Even OO languages can be message passing. Ruby method calls are syntactic
sugar over the send method of Kernel, which basically is the interpreter
itself. Examples at [https://stackoverflow.com/questions/3337285/what-does-
send-d...](https://stackoverflow.com/questions/3337285/what-does-send-do-in-
ruby)

And Smalltalk explicitly calls "message" what is sent to the destination
object [https://stackoverflow.com/questions/42498438/whats-so-
specia...](https://stackoverflow.com/questions/42498438/whats-so-special-
about-message-passing-in-smalltalk)

~~~
westoncb
> Even OO languages can be message passing.

IIRC, Kay said message passing was _the_ essential feature of his conception
of OOP in the first place, which was subsequently ignored by most—so 'even OO
languages' may be slightly misleading ;)

(Someone please correct me if I'm mistaken about this.)

~~~
jrochkind1
You are not wrong. Although Kay described what he thought of as the essential
feature(s) of OO in slightly different ways, "messaging" was usually in there.

> Smalltalk is not only NOT its syntax or the class library, it is not even
> about classes. I'm sorry that I long ago coined the term "objects" for this
> topic because it gets many people to focus on the lesser idea.

> The big idea is "messaging"

\--[http://lists.squeakfoundation.org/pipermail/squeak-
dev/1998-...](http://lists.squeakfoundation.org/pipermail/squeak-
dev/1998-October/017019.html)

> I thought of objects being like biological cells and/or individual computers
> on a network, only able to communicate with messages...

> OOP to me means only messaging, local retention and protection and hiding of
> state-process, and extreme late-binding of all things.

\--[http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...](http://userpage.fu-
berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en)

(I wasn't sure what he meant by "extreme late-binding", but Wikipedia says:

> Late binding, dynamic binding[1], or dynamic linkage[2] is a computer
> programming mechanism in which the method being called upon an object or the
> function being called with arguments is looked up by name at runtime.

\--[https://en.wikipedia.org/wiki/Late_binding](https://en.wikipedia.org/wiki/Late_binding)

That makes sense to me as part of transforming 'method calling' into 'message
passing' \-- the 'method name' you are sending isn't actually fixed to a
particular implementation that will receive it, until it is sent. Rubyists may
be familiar with this, as ruby indeed implements methods as 'message passing',
just like this.)

~~~
pmontra
You'll probably love to read this interview

[https://www.infoq.com/interviews/johnson-armstrong-
oop](https://www.infoq.com/interviews/johnson-armstrong-oop)

I quote from the first answer of Armstrong (Erlang)

> The 3 things that object oriented programming has it's messaging, which is
> possibly the most important thing. The next thing is isolation and that's
> what I talked about earlier, that my program shouldn't crash your program,
> if the 2 things are isolated, then any mistakes I make in my program will
> not crash your program. [...]

> The third thing you want is polymorphism. Polymorphism is especially
> regarding messaging, that's just there for the programmer's convenience.
> It's very nice to have for all objects or all processes or whatever you call
> them, to have a printMe method - "Go print yourself" and then they print
> themselves. [...]

> Erlang has got all these things. It's got isolation, it's got polymorphism
> and it's got pure messaging. From that point of view, we might say it's the
> only object oriented language [...]

By not crashing the other program he means two programs running inside the
same VM. There can be many programs running inside the same BEAM (Erlang's VM)
and they can be kept isolated via different supervisors. It's the normal state
of that system. Example: what in other languages is a library to make HTTP
calls in Erlang (or Elixir) is a separate process that receives messages with
the arguments of the calls.

------
_bxg1
It's funny how the language you primarily work with changes your perspective.

JavaScript supports "object spreading", which lets you do this:

    
    
      let foo = { x: 1, y: 2 };
      ...
      let { x, y } = foo;
      console.log(x + y); // 3
    

A common pattern is to use this as a stand-in for named/unordered function
arguments:

    
    
      function bar({ x, y }) {
        return x + y;
      }
    
      bar({ x: 3, y: 4 });
    
      // or...
      let foo = { x: 1, y: 2 };
      bar(foo);
    

Which is pretty much exactly the "epiphany" at the bottom (the
interchangeability of these two). I don't mean to diminish the author's
thought process; I just think it's fascinating how different programming
languages (even ones so similar in syntax) dramatically shape the way one
thinks about a given problem.

~~~
Waterluvian
Speaking of language perspective. I see obj spread in JS for arguments as an
ugly hack to make up for the lack of named and optional args. It gets really
ugly when you attempt to set defaults.

My perspective is from Python, which I think gets it very right.

~~~
adrianratnapala
But named-args in Python are also syntactic sugar for passing dictionaries
around. Object-unpacking of the the kind described here is another one.

~~~
Waterluvian
Syntactic sugar is the important part here.

You can always pass an object and handle defaults and default merging within
the function body, sure.

I use JavaScript a ton and I do love object spread and array destructuring.
But writing function signatures is still very clumsy.

------
mpweiher
Function calls are a special case of messages.

They are synchronous. Messages can be synchronous or asynchronous.

Reification and distribution are natural for messages. For function
calls...not so much. Procedure calls, a little closer -> RPCs, OK, but still
not really natural, as we have discovered over time.

One big one is that you can actively wait for/receive messages. I am not sure
how to map that onto a function or procedure call at all (not: "implement").

Dynamic/late binding again is natural with messages, whereas I am not sure a
function call with late binding is still reasonably a function call. Some
would say that the fact that the receiver decides how to interpret a message
is "declarative", and they have a point, though I am not sure I agree 100%.

Conversely: message sends are the generalisation of function calls.

~~~
dragonwriter
> Reification and distribution are natural for messages. For function
> calls...not so much.

If you have a way to call functions/methods dynamically by name, and a way to
store and pass tuples of arguments when doing so, reifying function/method
calls is natural, too.

~~~
mpweiher
Hmm...sounds like a message-send to me?

------
externalreality
Nice article! In order to "pass messages" you have to have two things and one
has to send a message to another - or one thing passing messages to itself. So
if you have a function and it reads in data off the program stack from another
function that is a form of message passing. I guess anything can be seen as
message passing. Today however, when I think of message passing it usually in
the contexts of threads or otherwise sequential processes communicating and
not a single process sending messages to different segments of program code -
and yet, as the article states, that is a perfectly legit way to think about
it.

------
Ace17
TL;DR:

"Calling a function with parameters is just another form of synchronous
message passing, either by-reference or by-value [...] But yes, we already
have message-oriented programming languages—if you squint the right way"

~~~
rzzzt
Reification is a nice feature of messages -- you can capture the "idea" of a
single function call as a message, so it can be logged, forwarded to another
component through the network, etc.

~~~
WorldMaker
Which is part of why the Redux patterns became so common in React apps,
because that message reification for logging and debugging is hugely useful.

That also strikes to the heart of a lot of complaints about Redux patterns and
their boiler-plate in that a lot of the raw details of that reification are
left to the developer (in the various action-creator patterns), as well as the
message bus dispatch (the various reducer patterns). There are usually
questions about why more of it isn't "just" single function calls.

