What's the Deal with Message Passing, Anyway
I'm back from the moving! Things are still a bit chaotic here but hopefully normal newsletter/essays/office hours should resume next week.
Alloy Workshop
Slots for the September 21st Alloy workshop are still avialable! Learn how to model domains in a way that finds the bugs for you, rather than three months into the project. Register here. Use the code YARLSNARTH
for $500 off.
(New this workshop: no more express train right outside my window. Reaaaaaaally looking forward to that.)
What's the Deal with Message Passing, Anyway
I don't know why I keep coming back to this topic. It's just so fascinating.
Alan Kay is the inventor of OOP and thinks in went horribly off-track. The key thing isn't the objects, it's the messages! In 2003 he wrote:
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.
I've already discussed how this claim is misleading. For once, though, I'm not going to dunk on Alan Kay's revisionism. Instead I want to talk about why message passing is genuinely unique and interesting.
Let's start with a typical OOP syntax. A method call would look something like this:
list.append(element)
Is there any difference between having append
as a method and doing something like append(list, element)
? You might have a separate object with the same method name, but that's handlable with namespaces or function overloading. Here, methods are no more than syntactic sugar.
There is one key difference, though, between obj.f(x)
and f(obj, x)
: where the source code is. In the former case, you could carry the code in the object itself as opposed to the global namespace. This is what Dan Ingalls talked about in his Smalltalk-76 paper:
The fundamental difference is that the object is in control, not
+
. ... In this way, meaning rides with the objects of the system.
This might seem like only sophistry... until you consider open-world systems. What happens if the object wasn't originally part of the source code? What happens if the object comes from the internet? Or a database? Or an SMS message? In "methods as function overloading" these are really hard to do: every time you import a new class of object you need to somehow figure out how to incorporate its functionality into your system. With "methods as message receivers", this all comes naturally: the object carries its own behavior, so sending the appropriate message gives you the behavior by default.
(Yes, you can emulate this functionality in a methods-as-functions codebase with appropriate patterns and serialization code. But that's emulation, not first-class properties. And you have to be clever, making it harder to use well.)
The other big benefit of message-passing is a bit more obvious when we switch to Smalltalk syntax:1
list append element: x
The message here is append element: x
, which is passed to the list
object. So far so good. Now in both models, we can do something like
# methods-as-functions
list.head().append(list.tail())
# methods-as-receivers
(list head) append element: (list tail)
The value we append is determined by a function call. Both can easily do this. But only in the message passing model can I do something like this:
list (obj foo) element: x
Here obj foo
determines the actual message we pass to the list! In SmallTalk, messages are also objects, meaning we can return them as values, meaning we can dynamically generate the message we want to send to our object. We could also dynamically generate the message fields in addition to the actual field arguments. This elegantly extends partial functions and higher-order-functions to object systems. As with open-world objects, this can be emulated without message passing. Python, for example, could do getattr(list, obj.foo())(x)
. But once again you're contorting the model in unnatural ways, which will cause more problems that you wouldn't get if you started with messages as a first principles.
Why Message Passing?
Message passing is a different approach than the "conventional" way most OOP languages use. As with most software innovations, necessity is the mother of invention. You don't need to be a brilliant visionary to come up with message passing: rather, Alan Kay was working in a context where it was a natural solution.
Kay first designed Smalltalk in tandem with the Dynabook, a prototype tablet designed for use by children. The Dynabook was supposed to encourage both educational use and programmatic use. Children should have the capacity to dynamically modify it and weave together its functionality. The Dynabook would only be successful if the child was both using it and modifying it. If a child downloaded an art program, they should be able to immediately use and programmatically interface with it.
This is an open-world computing environment. When you download a new app, the Dynabook doesn't already know what the behavior of the new app is. But it still needs to be able to interface with it seamlessly. And it's not enough to just run it as an executable, because that goes against the entire idea of the Dynabook.
Here's where message passing shines. With methods-as-functions you'd have to download the new objects and then recompile the system.2. In methods-as-receivers the behavior of the object is carried with the object and not part of the global namespace. If you download a new object you can pass a message to figure out its interface and from there use it with the rest of your system. No recompilation required.
It's important for us to talk about the context. Most people think of software innovations as coming from true geniuses. But people like Kay and Nygaard and Liskov are all just people, smart people, but still people. They come up with their ideas because they're trying to solve problems. Sometimes their solutions are broadly applicable, sometimes they remain limited to one domain. But it's still all in response to something. Understanding the problems helps us understand why they came up with the solutions they did and figure out where else these ideas are best applied.
If you're interested in trying Smalltalk, the most popular dialect today is Pharo.
Tweetstorms
Presumably you're on this newsletter because you like my writing. Right now my blog is for the serious, heavily edited essays and the newsletter is for shorter pieces.3 But I have a third writing venue: tweetstorms, for off-the-cuff rants. I figure going forward I will link some of my recent tweetstorms at the bottom of these newsletters in case you want to read them. Here are some of my favorites in the past few weeks:
- How I used math in my programming career. A nice followup to How knowing math helps you write better software. The newsletter was about how math is useful in principle, the tweetstorm is about specific practical cases.
- Why tools should hide complexity, and why "scaffolding" is such a critical pedagogical technique.
- What we mean by cleverness, why it's sometimes good, and why it's often dangerous.
- How Indiana almost made pi=3.2. It was part of a budget-balancing project. No seriously. Read the history.
-
This isn't a perfect map, I don't know the smalltalk syntax very well, just the abstract theory. Consider this a theoretical discussion of message passing as opposed to how Smalltalks specifically implement it. ↩
-
Again, this isn't always what you have to do, you can probably get around this with a whole bunch of emulation and cleverness. ↩
-
"shorter" meaning only 1300 words and 3 footnotes. What can I say, I like writing ¯\_(ツ)_/¯ ↩
If you're reading this on the web, you can subscribe here. Updates are once a week. My main website is here.
My new book, Logic for Programmers, is now in early access! Get it here.