Alan Kay had this to say about his coining the term “object-oriented programming:”
The original conception of it had the following parts.
- I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning – it took a while to see how to do messaging in a programming language efficiently enough to be useful).
- I wanted to get rid of data. The B5000 almost did this via its almost unbelievable HW architecture. I realized that the cell/whole-computer metaphor would get rid of data, and that “<-“ would be just another message token (it took me quite a while to think this out because I really thought of all these symbols as names for functions and procedures.)
I’ve cut this slightly short because the rest isn’t relevant.
One of Kay’s complaints about the modern world is that, perhaps because it was called “object-oriented,” everyone focused on the objects, but what Kay really had in mind was all about messages. It probably didn’t help that part of his notion of what “object-oriented” meant was “everything is an object.” So what would a message-oriented programming language look like?
So what are messages?
If you’re a reader of this blog from the beginning, you’ll have read my second-ever post “Data, Objects, and being railroaded into misdesign”. The big idea here is simply that many mainstream languages do not have very good support for representing rich data types, in part because they are object-oriented.
Following that, I wrote a 2-part series on the expression problem which pointed out we have three main choices when it comes to designing a new type:
- Objects, which present a fixed interface of methods, and are extensible in variants.
- Data, which present a fixed set of variants, but are extensible in methods.
- ADTs, which fix both variants and methods for users, but curiously permit the library author future-extensibility in both.
This choice is fundamental—it goes all the way down into mathematics. It’s not something we’ve accidentally dreamt up and will discard when we understand programming better. It is.
And if we take this choice seriously, what are we to make of Alan Kay’s preferences? They don’t make sense. Part of the problem is that we’re not really using compatible definitions of what “objects” and “data” are. Kay meant something different. But also part of the problem here is that I think this understanding of the relationship between data and objects is an advance in the state of the art. It’s a better way of understanding program design: one that is not incorporated into Kay’s vision. And it reveals a real confusion at the heart of that vision, when we take a critical look back on it.
There’s two major things wrong here:
- “Everything is an object” only hamstrings our ability to design well.
- Messages are inherently data, but Kay talks about “getting rid of data.”
I’ve written about the first already. But the second is the main observation I’d like to make in today’s post.
Kay likes to point to the internet as being something that’s inherently object-oriented in his sense of the term. But the very foundations of how the internet works is all about data: IP, TCP, UDP, HTTP, DNS, BGP, these are all protocols with very exacting schemas. Everything on the internet—every message sent—is data, not objects.
Now, my use of the word “data” like this is somewhat idiosyncratic. This way of understanding design is part of what I’m trying to communicate with this book project. I believe what Alan Kay has in mind when he says “data” is mutable data—shared state. (This is primarily based on his insistence that “data doesn’t scale” by which he appears to mean shared mutable data. I can’t imagine what else that comment might mean.) So rather than being inherently contradictory, it’s more in line with what functional programmers have been saying as well.
How the internet could have been built with shared state, in order to not be object-oriented in Kay’s sense, I’m not entirely sure. Perhaps like the circuit-switched telephones networks of old, instead of the modern packet-switched variety. That way the whole intermediate path has to take on some state, in order to make a connection. (On the other hand, we could argue that the internet is not a stateless as some like to pretend. What are BGP and routing tables, after all?)
What wrong with “everything is an _”?
Back when, Alonzo Church developed the lambda calculus. The basic idea of the untyped lambda calculus is that all of computing can be captured by this language that has nothing but functions. Literally, nothing but functions. Everything is a function. The number 1 is a function, and so are pairs, and so on.
Here is a
pair implementation, using a pseudo-Haskell syntax:
pair = \fst -> \snd -> \selector -> selector fst snd fst = \pair -> pair (\fst -> \snd -> fst) snd = \pair -> pair (\fst -> \snd -> snd) example = pair x y x == fst example y == snd example
One of the key reasons why this is possible is that other types can be emulated.
Everything might be just a function, but functions can closed over variables to construct data, like what
pair does above.
And a closure is just a primitive form of object: the special case of an object with just one method.
With more ordinary programming languages, we can turn objects into data with getters and the visitor pattern. Or we can turn data into objects with closures or virtual tables.
Each can emulate the other, so we only need one, right? So “everything is an object” doesn’t lose us anything, exactly, right?
But no. It’s an absurd idea to try to do real programming with the pure untyped lambda calculus. It’s Turing-capable, sure, but it’s not useful. Trying to use numbers is painful when you don’t have numbers, only functions emulating numbers. Not to mention slow.
So languages today are always at least a slight mix. Java might only have objects as user-defined types, but it admits a limited form of data: integers, floats, arrays, etc. Haskell might be very data-focused, but functional languages have always had the simplest object: the closure.
And of course, I lament the lack of languages that see real value in doing both well.
Messages must be data
But can’t we run with Church’s idea?
pair work, except by receiving a message and responding to it?
Isn’t that exactly what the implementation of
snd above are doing?
Sending object-messages to objects?
This is only an illusion. Fundamentally, all we can do is pass references to objects.
We can see this by thinking about recursive functions. If we tried to make copies instead of referencing, every recursive function would be of infinite size. We could never actually construct one to execute. (Well, insert quibbles about evaluation order here.) A recursive function is fundamentally an object with a reference to itself, if it had to actually contain itself instead of referencing, we’d just be staring at a contradiction.
And a reference to an object… is data.
We can go on about the implications of this. How are we storing things in memory? How are we writing things to disk? How are we sending messages between computers over the network? All of these are data. If everything is an object, what are we putting here? Are we tasked with inventing the One Serialization Method To Rule Them All? And if so, how are we going to inter-operate with anything that doesn’t play along with our demands?
So what DOES a message-oriented language look like?
Well, Erlang of course.
The thing about Erlang is that at its core it’s a purely functional language, only concerned with immutable data. Or at least, it’s that right up until you start communicating with other Erlang processes—by sending bits of data as messages. Each Erlang process is effectively an object, receiving and sending messages, collectively creating an interacting (concurrent) system, much like the biological cells that inspired Kay.
Erlang manages to be one of the closest things we have to Alan Kay’s ideas for what object-oriented programming should be, and it did it by… not “getting rid of data.” Everything is not an object.