They communicate by sending protein "messages" to their vicinity, and if a close-by bacterium knows what to do with it, it processes it, and responds by either sending out their own proteins (message passing), OR change their internals to get receptive to different kinds of messages (change internal state).
From this viewpoint, coding starts being about cultivating a literal society of objects (cells). The obvious advantage being that biology scales an immeasurable amount better than whatever architectures we ever come up with. He often gives the example of the internet as a well-designed system, it operates in a similar manner, objects (anything that can self-identify with an IP-address) can join/leave at any point, and the system itself stays alive.
I'm not 100% sure if this is what Alan means, but after having following his talks and writings for a couple of years, I think that's the gist of it.
It is such a horrible programming model, though.
Making calls and sometimes they get picked up and sometimes not?!?
If we're talking remote processes, sure, that's understandable, remote resources are not always available.
But in-process? If I call a function on an object, I want the compiler to tell me right now if this code makes sense, and the compiler should stop me right away if that code cannot possibly work once deployed.
Rather, it's about your object not having to know who is handling the message, only what the message is ("what" in both content and type).
It's still up to you as the programmer to make sure that you have implemented the other objects to act on whatever message types your code will pass. Just like it's up to you to make sure the method exists in the target class when the caller tries to invoke it.
Later on, if you decide to swap out your logging infrastructure for a different one, you don't need to update all the places that called the old one. You just tell your new logger to listen for messages directed at Logger.
Except that is the bacterial model. When you want something done, you signal for it by releasing proteins, and keep releasing proteins until you're satisfied even if the work ended up being done 50 times more than you needed. That's why biological systems so commonly overreact to stimulus. The biological method is to flood communication until whatever demanded response is met. It's the equivalent of a user mashing a button until a program responds, like the elevator button problem.
Actually, elevators seem like an extremely good analogy for this kind of asynchronous service system.
I understand we're not trying to perfectly mimic the analogy, but I think it's important to see that nature's model, while robust and asynchronous, carries significant problems due to how it communicates. It's that very robustness that we're trying to mimic, so we should expect to inherit some of the problems that go along with it.
Though, as GP stated earlier, this does make some more sense from a distributed networking perspective, where no machine has control over the existence of its peers. In that case, having a setup where the message is sent out on a service bus to be snatched up by a compatible listener is closer to the biological analogy.
There's still some down sides in that now you can easily get race conditions on human error, you're dealing with distributed systems problems. You have to worry about defining your communication interfaces and keeping them updated, and resilient to change. Message passing has a lot of trade offs.
I like that paranoia is the default. Once you get used to it, easy stuff remains easy and hard stuff becomes less scary.
> But in-process? If I call a function on an object, I want the compiler to tell me right now if this code makes sense, and the compiler should stop me right away if that code cannot possibly work once deployed.
You are going of a tangent; What you are refering to is a matter of typing and compiler help, and not related to the OO paradigm as thought by Alan Key. (If there is a relation, I would be happy to learn it). Put more concretely: I don't see why a Java compiler would be more warning than a typed Smalltalk compiler.
It's an honest programming model that can deal with real life. A lot of modern systems converged to Kay's OOP model, except the implementations are implicit and created by continuously tripping over problems that were already solved in the 80s.
JavaScript is SmallTalk minus elegance, minimalism and orthogonality.
Web pages with JavaScript are self-interpreting data in the exact same sense Kay's objects are.
Microservices are crude, bloated objects that communicate through routed messages.
Containers are a validation of Kay's idea that sharing encapsulated objects is easier than sharing data.
Most people commenting here about SmallTalk either never watched any of Kay's talks, or simply are too dumb to understand what he is talking about.
>But in-process?
Most of the real programming problems these days are not in-process.
The interesting thing with the bacteria/biology metaphor is that the time scale variation is huge, from micro to macro.
Your compiler is life itself and the validation is survival, with evolution.
Sure, it may not be enough to be practical for computer-based stuff, but for a resilient/scalable system, that's a very interesting and enlightening angle to look at.
It's the basis of mainstream OOP as well. When you make a method call, you only know by loose protocol what effect it has, which objects it has an effect on or even whether it has an effect at all, as opposed to manipulating the data structure directly. It is the recipient of the message that decides what effect it has. This doesn't preclude having some mechanism to tell whether the message was accepted or not.
IMO the significant difference between something like Smalltalk-style message passing and Java-style method calling is that an unknown message in Smalltalk is a run-time "error"—which is passed as a #doesNotUnderstand message to the object that invoked the original message—while in Java it can be checked statically because the object definition specifies what messages it somehow handles.
A simple one-way message passing OO architecture in C, using a global set of messages:
#define SEND(_objp, _msgp) (((struct Object *)(_objp))->dispatch((struct Object*)(_objp), (struct Message*)(_msgp)))
enum MsgStatus list_dispatch(struct Object *self, struct Message *message)
{
switch (message->method) {
case MSG_INSERT:
insert_into_list((*struct List)self,
((*struct InsertMessage)message)->index,
((*struct InsertMessage)message)->value);
return STATUS_OK;
case MSG_DELETE:
// ...
return STATUS_OK;
default:
// Maybe the parent implements the message
return SEND(((*struct List)self)->parent, message);
// or we decide that this isn't a valid message for this object
return STATUS_NOTUNDERSTAND;
}
}
// ...
// Construct and send an INSERT message to someList
createInsertMessage(&insertMessage, 0, "hello");
status = SEND(somelist, &insertMessage);
Now, every object is encoded by a struct starting with an Object struct which contains a generic dispatch function pointer, so that each object can encode their own dispatch logic and handle whatever messages they receive as they see fit. In this case, the ListMessage struct also has a parent field, which it defers unknown method calls to. If it did not, it could just return a method-not-found status code. It doesn't have to know whether the parent implements the message.Likewise, every message starts with a Message struct which contains the message type/method name. Depending on the type it can be cast into more specific messages.
An interesting aspect of this architecture is that it doesn't explicitly implement any kind of inheritance logic. You let the objects handle that themselves. A possible benefit of having any object accept any kind of message is that you decide whether it's an error that an object could not receive a message or not. Maybe you don't care that the object couldn't receive e.g. a NOTIFY_CHANGE message.
I just wanted to demonstrate why message passing via dynamic dispatch isn't so fundamentally different from static dispatch, conceptually, and to which extent it really is "making calls and sometimes they get picked up or not" compared to any other dispatch mechanism.
Kay might disagree with this on the basis that late binding is important and that an object should not need to know what messages it will be passed at run-time; that the message is just that, a message rather than a contract. The only contract that exists is that an object should be able to receive a message, any message. But even with checked method calls like in Java I think the fundamental idea that you shouldn't have to consider what effect a method has on an object remains. What a language like Java adds is contracts like abstract classes or interfaces, essentially a way to tell the type checker what messages may be passed to an object.
I agree that this approach can be useful in some cases and also think that you most likely not only AGNI, but that it will cause serious headaches and existential dread when applied to the wrong problem. So will Java. IMO OOP is suffering a bit of a backlash not because it's a particularly bad, but because like a Swiss army knife it looks deceptively applicable to a wider range of problems than it really is.
But isn't this a pretty good working definition of "Object Oriented Programming"?
Not at all!
If I call a.foo(), I know for a fact foo() will be called. That statement is not just going to be ignored and dropped on the floor.
What it will do, now nobody has any idea, whether the call is local or remote.
Likewise, when you send the message foo to a, you know it will be received. In both cases, the only way you'll ever have an idea of its effect, if any, is by knowing the state and implementation of a at the point of sending the message or calling the method.
What is different is how you communicate that a method might have had some effect. In Java I'll know at build time whether a method definitely wasn't considered because of its type system. It won't compile if I have not defined the method being called. In a dynamic language like Python, calling a method that is not defined results in an exception. In Smalltalk, an Object receiving a message with an unhandled method will send a #doesNotUnderstand message back to the sender. The default behavior of an Object receiving the #doesNotUnderstand message is to raise an exception. Unless you explicitly override the #doesNotUnderstand method, this becomes an implementation detail and really isn't much different, especially compared to dynamically typed languages with OOP facilities.
So as I said before, the message passing mechanism doesn't preclude communicating whether it was handled. Messages get ignored if that's what you want. Java IMO really has the same potential for run-time uncertainty thanks to exceptions.
People tell me Ruby is message passing, but it doesn't seem to match your description at all.
Alan, if you're in the comments section I'd love to hear your thoughts on what you think of the resurgence of functional programming in the modern day and whether anything, other than perhaps languages like Erlang, has approached your original ideal.
I just watched the whole thing, based on just seeing your link.
It does talk about the historical connections between Smalltalk and Erlang, but goes far beyond that to remind us to think more about the bigger problems we need to solve, and get less caught up in doing things just because we have a tool that can do them or other people are doing things that way.
I found it truly inspiring.
Someone asked a similar question when he did an AMA, but the answer wasn't clear to me:
To me, it's the difference between pulling the strings on a puppet (method calling) vs placing a request to a colleague (messaging).
However I don't think anything in the Ruby docs ever claims that it has a message-passing intent. I think the option to write in this style is really a byproduct of duck typing combined with a Ruby object's ability to arbitrarily and dynamically reconfigure its own dispatch table. If Matz really intended a message-passing style, then Object#send would've been split into Kernel#send and Object#receive, and there'd probably be a Message class rather than just using symbols.
Given the new shorthand of object.:name in 2.7 for object.method(:name) one could even argue that Matz is drifting Ruby back towards an early binding style. However I don't see myself using that syntax much outside of block parameters to Enumerable methods and the like.
Overall I think the stylistic options are a strength and we don't particularly need to resolve the dichotomy to write good Ruby.
There is of course the `Kernel#send` method, which implies the model is messages being sent. It's not called `call`.
The fact it isn't invoked for regular method invocation is kinda indicative of Ruby _not_ being a pure message passing language, even if we like treating it that way.
It's definitely Kernel#send. Object defines no instance methods at all.
> Kernel.instance_methods(false).include?(:send)
=> true
> Object.instance_methods(false).include?(:send)
=> false
> Object.instance_methods(false)
=> []
https://github.com/ruby/ruby/blob/v2_6_2/vm_eval.c#L2163That's a somewhat arcane implementation detail though. What about the meat of the discussion?
If I'm being uncharitable I think applying the term 'message passing' to Ruby is a bit of making something sound more sophisticated and elaborate than it is. They're method calls - it's pretty simple already and doesn't need a complex concept on top of that.
It’s a little tough to compare to an OO language because there are no classes, but all object to object communications are asynchronous messages.
This is the way I think about it as well, the more concise notion is ask don't tell. By don't tell I'm thinking of telling an object to change it's state directly. This distinction can be very, very subtle.
It's a bit more involved than that - modules can be included and prepended. In a sense "re-arranging the which cells are close and further". Also `method_missing` can be implemented. Beautiful in my opinion.
RailsConf 2015 - Nothing is Something
https://www.youtube.com/watch?v=OMPfEXIlTVE
But to me, this simply looks like Functional Programming done in an OOP mess.
Map, filter, flapmap, lambdas, symbols, are all there.
You can easily do LINQ in Smalltalk-80 without any additional library.
The premise was to start with a standard class-based instance, and then use metaprogramming to autodefine a corresponding ADT for all possible typed responses from the API.
This gets around the main problem of message routing (IMHO): Namely, if you're relying on a host system to interpret your message how it sees fit, then you don't really know how it will respond in a given case. Will it error? Will it call something different than what you thought? The solution is just to enumerate every possible response type, and return a corresponding composite type as an ADT. I was doing that manually in a few prior cases, and then realized I could use the macro programming features in Haxe to do something automatically, and put together a library as a POC.
I'm certain that FP purists would argue that this is equivalent to some other pre-existing type transformation... but I haven't found it yet. I'm interested if anyone else has been experimenting in this area.
Not sure what you mean by this.
In the general case, the hard part of "scaling" is building systems that are easy to reason about / maintain as the complexity / throughput increases.
Meanwhile, biology is notoriously difficult to reason about and maintain (think medicine).
When one dreams of a biological approach, one is interested in replicating the "graceful degradation" aspect of biological structures, which is the opposite of brittleness. If you remove one random cell from your body, it will make virtually no difference. If you remove 1K random cells, you will still not notice anything. If you remove 1M cells, maybe it's not so nice, etc. Biological systems have fewer central points of failure and are much better at adapting to unexpected situations. In this case, complexity can be our friend. I believe it is possible to learn how to design systems more like nature does, and I think this is Kay's dream.
It's obviously a bit wooshy, but I'm pretty sure that's the gist of what he intends to get across.
Alan Kay is, of course, correct that nature provides excellent guidance on how to build resilient systems. I would think that thought is fairly self evident to the subset that is preoccupied with such thoughts.
But as the endless exegesis of still-with-us Dr. Kay's historic utterances here and elsewhere demonstrate, there is this very obviously missing link (or unkindly 'hand waving') between "his" 'big idea' and how could we possibly build bio-morphic computational systems with the current state of the art and craft of building software. At which point one could ask the question "why are we discussing these wooshy notions?"
The why is not to be practical right now, but to research these ideas to the point where they do become practical, no?
But then again, I agree with you, he does keep going on about the same ideas again and again and again, without pointing to the slightly more practical details... Basically ever. It's always "no what we do is bad, all of it, haha don't we suck?" "Look at these cathedrals and ant hills! Why can't we do that???"
I do agree. Vision can furnish the impetus. A good "Research" effort must bear fruits, and result in "findings".
For example, in my mind, what is not addressed by Alan Kay is whether there will be any meaningful distinction between software and biology in an age where his vision is realized. Clarification (or development) of this key point would inform research efforts.
A second problematic aspect has to do with developmental methodology. Should we be working at the genotype or phenotype level? If the former, see above question. If latter, is not raising cattle somewhat analogous to tending to your objects? (Meaning: cattle herders are not biologists ..)
Related to this is the issue of scale. Biological systems are indeed majestic, and no wonder: look at the range of scales of biological mechanisms! Even our current wonder crust of layer upon layer of the net's stack amounts to nothing more than an active biological membrane buried somewhere deep in some micro corner of an 'biological organism'. The biological organism is stupendously complex and its component elements' sizes range from micrometers to meters. We have 10^13 cells in our bodies. And a single cell is by itself a marvel of complexity.
Are those scale ranges and complexity orders necessary for biological magic? Are we to attempt this with code on digital processors?
And let's not even discuss the various interactions that a single biological organism has with its environment and fellow creatures (at multiple layers of organizational structure, concurrently!).
And here, finally, we arrive at the actual wonder factory, an ecology where selection comes into play.
It would seem, at this point, to concede that perhaps the 'cattle ranchers' may not be so wrong headed, after all.
To better understand what Alan Kay is talking about it helps to know a specific fact about Smalltalk, the language he invented. Unlike in C++ or Java or JavaScript Smalltalk objects can not have public data-members. They can only have methods. Methods can read and write the "instance variables" inside the object, no-one else except the object itself can.
This means that objects can only communicate by calling methods. They can not read or write data stored into another object. Thus they are totally ignorant about the "implementation" stored inside other objects. They can "send it messages" meaning call its methods. But because all communications with an object are processed by the methods of that recipient object, the recipient object itself is wholly responsible for the MEANING and effect these method-calls have. "Message-passing" is an apt metaphor for such a thing.
This is "message passing" because it is the recipient object who must ALWAYS INTERPRET ALL messages it receives. Therefore method-calls have the semantics of "messages".
Whereas in Java and C++ and JavaScript the caller of an object can also directly modify other objects without the object being modified being "aware" it is being modified. The "meaning" of such modifications is then determined by whoever makes them. Say I store into your object the property "you.age = 253". But what does that mean what does that signify? Only the other object which stored that value "knows" why it did that, what it "means" what implications it should have for the later progress of the program.
So even though in Java etc. you can implement message-passing simply by calling methods, you can also do other things to the object from their outside than only send them "messages". An Object-Oriented language according to Alan Kay (according to me) then is one where the only way objects can communicate with other objects is by calling methods i.e. by message passing, never by modifying the objects directly from their outside.
In Java, many projects overuse getters and setters. Getters and setters break encapsulation. More so public setters since they allow any piece of code anywhere can change the value of an object's internal state at any time.
Consider a class Person. A person has a name, SSN, salary. Let's assume name and SSN cannot be changed. What about salary? We don't have to add a setSalary() method. Instead, we should have something like: public void acceptNewJob(Job newJob) { salary = newJob.getSalary(); }
Yep, and that’s pretty much the simplest and most modular design for that problem.
This is one of the cases where OOP(whether using methods or messages) leads to more coupling and less flexibility.
I do like your use of the word "anemic" to describe the method free classes that pass for objects these days.
Everything you say is confusing to me, but I'll just pick on one:
> An Object-Oriented language according to Alan Kay (according to me) then is one where the only way objects can communicate with other objects is by calling methods i.e. by message passing, never by modifying the objects directly from their outside.
What guarantees that when I send a message to an object, that object won't decide to change itself in response?
In other words, that distinction you are trying so hard to build between message passing and function calling is nonexistent.
I don't think that's what the other commenter is saying.
They're saying that internal state is not directly manipulable from external sources. So an object `x` cannot have a method that takes in another object `y` and does `y.foo += 3`. Instead, it must do something like `y.addFoo(3)` to accomplish the goal.
This is superficial in some sense, but then every distinction between various programming languages is superficial if you look at it right. :)
The idea is that the object itself is the only thing which can manipulate its internal state. Therefore, to use these objects, the programmer must have imbued them with the necessary methods. This is restrictive (you have to implement the methods before you can manipulate the state), but this is the core design principle I think the other commenter is trying to tell you about. Objects cannot be manipulated willy-nilly; they must have implemented some method which can be called from the outside. This restriction creates a much more solid barrier of distinction between the responsibilities of the various objects at play.
Control structures
Control structures do not have special syntax in Smalltalk. They are instead implemented as messages sent to objects. For example, conditional execution is implemented by sending the message ifTrue: to a Boolean object, passing as an argument the block of code to be executed if and only if the Boolean receiver is true.
The following code demonstrates this:
result := a > b ifTrue:[ 'greater' ] ifFalse:[ 'less or equal' ]
For example in Kotlin:
class Boolean {
fun ifTrue(closure: -> ())
fun ifFalse(... whatever)
}
a.ifTrue { /* do something */ }IIRC, Smalltalk has Boolean class and two subclasses of Boolean: True and False. There is a single method with two arguments (:ifTrue:ifFlase). The method is then overloaded. True calls ifTrue argument. Flase calls ifFalse argument. This is happening dynamically, at runtime. Again, the mechanism is generic enough to fully replace all use cases of "traditional" if/else constructs.
Clearly, you haven't thought this through.
Edit: People here would do well to read this: https://pozorvlak.livejournal.com/94558.html
----
My best guess, especially given Alan Kay's statement "I wanted to get rid of data" is that it is more of a style of coding than a technical distinction. I could be misinterpreting him and it would be nice if he would mention a small and concrete example that illustrates the True Meaning of Messages.
I see it as the style of coding you run into reading AST-processing code in Java, where, because the language lacks discriminated unions and pattern matching, you don't simply look at the `expression` object you're given and see that it is an `AdditionExpression(LiteralInteger(1), VariableName("x"))`. Rather, you politely ask the expression to describe itself to your own `visitor` object, and the structure of the expression reveals itself by calling `visitor.VisitAddition(leftSide, rightSide)`, and the left side calls `visitor.VisitLiteral(1)` and the right side calls `visitor.VisitVariableName("x")`. Data has been reformulated into a series of calls.
That is the same pattern of coding as having booleans be defined by whether they call the ifTrue or ifFalse branch.
Subjectively I despise programming that way and much prefer using a language that lets me define my data structures immutably and precisely without code, then process them with compiler guarantees that I handle all cases. Reading the data types is the fastest way to understand what a piece of code is trying to accomplish. As Fred Brooks said:
> Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious.
(a == 1).ifTrue {
// ...
}.else {
// ...
}
is standard Kotlin with its DSL syntax.`ifTrue` and `else` are extension methods added to the `Boolean` type.
You know that there have been new developments in PLT since Smalltalk, right?
You don't seem to understand what this discussion is about. Extension functions in Kotlin are statically dispatched, so while they are a nice feature, they are completely irrelevant here.
It's not about how your invocation code looks like. The important part is that at some point the code needs to make a decision whether to invoke "if" case or "else" case. Smalltalk achieves this by having two objects/classes (True and False) that handle the same message differently. The implementation of those objects does not have a hidden control flow statement. Your code would.
General-purpose programming languages, by their very nature, cannot provide us with constructs that are specially suited to our domain; they can only provide us with generally-useful building blocks, like booleans, integers, functions, etc.
We could try to solve our problems using only those languge-provided constructs directly, e.g. using maps-of-lists-of-booleans-of-whatever. In that case, we get "callback hell", pyramids/triangles of doom, etc. because the same code needs to implement our solution and encode information about our domain.
Alternatively, we can use those language-provided constructs to write our own domain-specific constructs; then use those domain-specific constructs to solve our problem.
I've worked with people who avoid this second approach because understanding the solution requires learning those domain-specific constructs, whereas in the first approach we already know how built-in constructs like booleans work.
The nice thing about Smalltalk in this example is that if/then/else, loops, etc. are not built-in; they're library code. This takes them off the pedestal that they occupy in other languages, and makes it easier to think about replacing them with our own tailor-made alternatives.
(Note that this isn't specific to message-passing style OOP; we can also do this with e.g. recursive functions for loops, induction schemes (e.g. Church encoding) for control flow, etc.)
In short, the distinction between a system with method calls and one with message passing is whether or not it allows for spooky action at a distance.
But this distinction isn't really carried through by all the systems that claim to be based on message passing.
Say I look over my cubicle wall and see you and "send" you a paper-airplane. The paper-airplane is the message, I can assume you will become aware of it having landed on your cubicle. Perhaps there is a question written on the paper. Because I have good eyesight I can spookily already see your answer without you having to send that paper-airplane back to me. It's still message-passing even though it is spooky.
The real distinguishing feature of "message passing" I think is that the recipient is free to interpret the messages they receive, and that no-one else can, that no-one else can modify the "mental state" of the message-recipient than they themselves, as a response to the messages they receive.
If you're really only allowed to communicate by passing messages, then, to me, that would imply that there's some sort of "no peeking" rule.
I would say no because you are not communicating with me at all. I am unaware that you have modified the object.
I can ask that object about its state and that way detect that somebody has changed it. But how can I ask it about its state? Only by sending it a message/calling its method, IF I am using an "Object-Oriented Language".
There is nothing spooky about multiple observers having access to the same shared mutable object, is there? They can still only extract information from it by - sending a message - in an Alan Kayesque Object Oriented language.
But in a hybrid language like JavaScript where I can directly read and write and even delete fields of an object it is spooky, this value was just here and now it is no longer and I don't know why because somebody else somewhere deleted that field.
There's maybe a "letter of the law vs spirit of the law" thing going on here, too. Having an object that just implements an array feels like it's violating the basic idea, even if you are technically only communicating with it by passing messages. I'm pretty sure the original vision was that objects would represent interesting entities from the business domain and not just be heavyweight analogues for basic data structures.
(Just don't ask them about the ets module.)
The reality is that most of the time when the world is dynamically changing, there are reasons for it.
E.g. the typical Ruby example is an ORM where the methods available reflects the database schema of the database you just connected to. Whether you get an exception because the method you expected doesn't exist, or get an exception because some generated method got an error from the database doesn't matter: You need to deal with it either way.
All that changes in practice is the name of the error condition you need to handle.
1) that objects are acting like async, independent machines (I picture the erlang actor model, but maybe that’s a stretch.). This makes messages a more natural way to emphasize that these actions can happen across networking or threading boundaries;
2) the message is explicitly not inheritance and therefore doesn’t contain implementation. This means it’s really a messaging contract and only the interface matters!
3) redirecting, storing, or replaying messages are each very powerful concepts that lead to many useful features (networking state, save/load, undo/redo, logging, debugging). Thinking in these terms is much harder to express with functions, so I see this as messages are the _data_ of function calls, and function calls are more the _act_ of calling.
That said, I’d love to hear other’s take on this as well. The differences from functions are subtle and it doesn’t help that we use the same/similar words to mean different things.
In the types of OO systems Kay discusses, it's up to the receiver to determine whether or not to respond to a message and how to behave if it doesn't "understand" the message. That's how you get maximum polymorphism as well -- for example, all kinds of objects are "cancelable" if they respond to / understand the message #cancel, etc, etc
This seems exactly analogous to how a collection of webservers interact. They can only communicate with each other by tossing a chunk of bytes around hoping that the target knows what it's supposed to be doing with those bytes.
It's a horrible programming model, though. We should be glad this is not how most languages work.
Have you ever actually programmed in this model? I wrote a correct RAFT library in ~300 lines of code that are extremely legible using this model.
This is not only about languages, but also environments. The argument Kay makes is that in order to make very large complex systems that are resilient, you need something like this kind of message passing. And indeed, this is how the Internet works too
Objects in contrast are not stateless things. They can know the history of the messages they have received earlier, as reflected in their internal state.
The distinguishing feature of message-passing an OOP I think is that the receiver of a message is free to decide how it will respond to it. Will it always return the same result for the same inputs? Not necessarily. I decides. It is the decider.
One way to think about Objects is as independent computers which get inputs from their surrounding and then somehow process such inputs. They might not return any result, but still do something useful. The point is they a PROGRAMMABLE objects, they are "live".
A message can be asynchronous. It can also be synchronous. You can persist messages, replicate them. Distribute them.
With a method call, the "caller" control what code gets executed. With a message-send, the sender politely asks the receiver to respond to the message. Is there code executed? Maybe. Maybe not. Maybe the message is ignored.
So something like Smalltalk is (roughly) at the minimal end for the capabilities of "messaging" whereas it is either at or slightly beyond the maximal end for "calling a method". The fact that there is a slight overlap makes things difficult, but not impossible, to distinguish.
In the articles on Software-ICs[1], they call the flavour of OO "object/message"
"Sending a message to an object is exactly like calling a function to operate on a data structure, with one crucial difference: Function calls specify not what should be accomplished but how. The function name identifies specific code to be executed. Messages, by contrast specify what you want an object to do and leave it up to the object to decide how."
I am not sure I'd agree with that 100%, and there are tons of ways to argue against it, but I think it's a start.
[1] https://blog.metaobject.com/2019/03/software-ics-binary-comp...
I've come to learn that a lot of us think about creating classes first and the conversations second. Conversations and the messages in these conversations should, often times, tell us whether creating a class right now is needed. I really follow the philosophy of putting off decisions to lock oneself into an abstraction too early on (not enough information/data) and preserving flexibility. Often times, a struct is enough for carrying pieces of a related idea around. It isn't until its internal state or a need for it to have conversations with other objects do methods defined in a class make sense. Furthermore, classes without methods are just state containers.
I write some Ruby and some of the basis of my ideas come from Sandi Metz's book, "Practical Object-oriented Design in Ruby" where she describes message-passing.
… A crucial property of an object is that its private memory can be manipulated only by its own operations. A crucial property of messages is that they are the only way to invoke an object's operations. These properties insure that the implementation of one object cannot depend on the internal details of other objects, only on the messages to which they respond.
Messages insure the modularity of the system…"
pages 6 & 7, [pdf] Smalltalk-80 The Language and its Implementation
http://sdmeta.gforge.inria.fr/FreeBooks/BlueBook/Bluebook.pd...
1. Make a logging proxy that wraps an arbitrary object. Any method you call on the logger, it logs the name of the method, and then invokes that method on its wrapped object.
2. Make a "tee" object. Any method you call on the object, it invokes on its N child objects.
3. Make a "replay" object. Any method you call on the object, it saves instead of executes. Later you can replay the saved method calls by redirecting them onto another object.
Message sends can invoke a method, but they can also go beyond that.
You say somefunk.call (someObject, ...someArgs)
The function executes in the context of 'someObject' meaning it can read and write its fields with expressions like 'this.color = "blue"'.
You could say that somefunk() is a method and you are making a method-call. But it is not a method of 'someObject'. Yet by calling it you CAN manipulate the fields of someObject.
JavaScript is NOT the type of "Message passing/Object-Oriented" system like Smalltalk-80. You can communicate with objects, meaning share information with them, meaning read and write their fields, by means other than their methods only.
The core difference is that messages are interpreted by the object itself. At runtime.
Here is a practical example where the difference is obvious. Let's say you have an object that does business logic. You want to log all of its operations without changing its logic or the code that calls it.
In a method call language, this is a pain in the ass. You have to subclass the original class, override every single method, and call the original logic + logger in every single one of those methods. And after all that work all you get is a lousy logger that's just good for one class and needs to be modified if you add something to the original class.
In a message-passing language, you would create a proxy class that passes all of its messages to the original object, then calls the logger. This implementation would involve writing only one method and would be applicable to absolutely any class/object in the system.
And this is just one out of many, many instances where message-passing allows for elegant solutions while method calls make you miserable.
Obviously it is a definition of normal method calls in Smalltalk.
Even with Smalltalk, he well or poorly do you think setters/getters fit within that description?
One thing though "tru OO" guys often complain of false message passing based on function call. Others here are hinting at asynchronous vs synchronous. Very likely to be what Kay meant.
Another thing to me, is that message interactions mean standalone data exchange. As if to design your interfaces to accept very small but complete objects as communication protocol. Rather than passing structures to be modified. I'm not sure it is even true at all but that's a feeling I got from Kay's talks and his biology metaphor. In a way it's a stateless bus connecting stateful objects (probably trying to minimize their internal state space as much as possible too).
Method calls in C++ or Java are rather primitive, in comparison. Though of course you can build/emulate all of that on top of either. After all, the Erlang VM (BEAM) is written in C...
There are at least two problems at work here:
1. The words that Alan Key is (rightly) using are overloaded, since other languages adopted the OO term, but interpreted them differently. This adds to miscommunication: The words sound familiar, but their meaning might be subtly or drastically different.
2. The advantages of OO as thought by Alan Key are hard to convey if not by hands-on experience. Debugging and developing programs in Smalltalk is really fun and effective, but I had to write my thesis in a Smalltalk company first to appreciate it.
This is an instance of the "blob problem" as termed by Paul Graham. Many Non-Lispers have a hard time appreciating the benefits of powerful metaprogramming, and reading articles about it won't change that. They might peak interest, but one has to play around some time with those concepts to gain a feel for what benefits and limits are.
A GET request was originally envisioned as a request for a file at a path. `http://host/get/this/thing.data` The requestor decides what to do (give me this file), the host obeys.
Nowadays we understand GET requests as abstract. The requestor makes a request, but it is the host who decides what to do with it: forward it somewhere else, emit an error, dynamically generate the content, return a real file, serialize and cache the request, etc. The client doesn't know and can't know what the server does.
Notice in particular that hosts are expected to handle arbitrary URLs sensibly.
Java has no support for this, but Ruby and ObjC have these sort of facilities: forwarding, dynamic replies, etc. It's up to the libraries to make good use of it.
Nope, a very early description of HTTP – years before 1.0 – already mentions that.
https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/...
For one, the binding happens much later.
Second, messages you can't handle can be automatically forwarded and delegated.
Third, it doesn't have to be local, the receiver could be a server in the other side of the world.
Redux action dispatchers are kind of messages...
So I'll explain what it means here, because it took me a lot of years to understand that "late binding" was a specific concrete concept rather than a vague generality.
What we're talking about when we say "binding" or "linking" is the process of associating a reference — typically to a function that's being called, though occasionally to a global variable or something — with a particular referent, such as a particular chunk of executable code. When we say that some "binding" is happening "late", what we mean is that this linking is happening at run-time — at the time of the actual call, not even at program startup time, as with ld.so. Doing the linking (or "binding", as some people called it 40 years ago) at call time means that the same function call can invoke a different function every time you call it.
Of course, that's what method calls in languages like JS, Lua, or Python do, as well as function calls in languages like Scheme, JS, Lua, or Python. By contrast, calls to C++ or Golang methods are compiled into machine-code calls to particular machine-code functions, unless the methods are virtual or via an interface, respectively, and it's the linker that links (or "binds") these calls to their callees. (Unless they're inlined, blah blah.)
This, of course, gives you immense flexibility, at the expense of some performance and predictability.
You're right to distinguish message passing from late binding, as message passing is more general.
With message-send semantics as in Smalltalk or Objective-C, the compiler only knows the method's name, and calling a method foo compiles to the same thing irrespective of the object's class. It's the object's job to determine what foo means, and it will do the dispatch to determine which method to use, or if no method matches, call a missing-method handler.
You could compile Objective C with a profiling or global analysing compiler that compiles method calls to different things depending on the class of the object if you wanted to, and you could compile Java with an approach that compiles to the same thing regardless of the object's class if you wanted do.
But message passing is always described as a programming model, not an invisible implementation detail.
Also implicit in message passing is a form of structural typing; objects that understand the same set of messages (called a protocol in Smalltalk-speak) are type-equivalent. A client class may delegate to any object that understands the messages the client sends to its delegate. One of the advantages this brings is all the crazy things you can do with #become: in Smalltalk. A perfectly valid way of responding to a message you haven't implemented is to construct an instance of a class that has an implementation for that message, and then become: that instance, swapping all references to yourself in system memory with a reference to the instance you created before passing the message onto the instance (your new self). There's no way to implement general #become: in C++ or Java because there's no guarantee the result will be sound type-wise.
The changes are subtle but they open up a world of dynamism that Java programmers don't have access to. Which is fine for Java programmers, who decided they don't want that kind of dynamism anyway, but there are programmers who work better shaping a live system rather than declaring type ontologies in advance. And Smalltalk is designed to work well with such programmers.
Are you telling me that if I turn on a compiler optimisation my message passing program suddenly becomes a method call system?
So the difference cannot be what it compiles to.
I’d say messages are very loosely coupled compared to a function or method call.
In c everything is laid out by the linker. You can play games with function pointers, of course, but that is tricky.
Message passing isn’t really resolved till the message is actually sent. Late binding I think it’s called. Objective c gives so much control, you could add remoting at dispatch time.
I think Java’s methodinvoker is pretty much it for Java. You can implement an interface at runtime. But you don’t control dispatch quite as deeply.
Micro services approximate some of Kay’s ideas really well, but imho they give up too much control over dispatch. In ruby or objective c you can hijack the dispatch system in a way that’s really hard to do with micro services
Messages differ in that you are only providing a description of what you want the object to do. The object then interprets the message and decides how to respond. That may mean calling a method that matches the description, but it may choose to do something else entirely. An object may even handle messages that have no corresponding methods at all.
The big idea is that an object can reason about the message before it handles it.
I can not call a method that isn't there.
In any event the caller must not be specifying the function to call (i.e. it's a virtual method in C++ terms) and it should be possible to send a message that is not understood without causing a fatal error (requires reflection in many languages).
I do that sort thing in C via a SendMessage(msgType...) idiom. Instead of a bunch of functions, you have one function and a message Type and some data. If the destination doesn't know what to do with a message of type whatever it just spits that back.
It’s just an idea. It seems to work well in some situations, given the successes owed to Erlang and dev architecture like microservices.
People have their war stories about terrible experiences too.
Now imagine something like Erlang, which is a complete mind screw for someone used to writing sync code. The FooProcess can send the BarProcess an async message, but there is no guarantee the message got to BarProcess recieved the message, or that the BarProcess or that it will even be alive when the message arrives.
Maybe a good place to start is to read up on method_missing and message passing in Ruby.
If we're talking remote calls, sure, there is never any guarantee that the recipient has that method, or that it's even listening anyway.
All of that to say that that whole discussion between function call or message passing is a waste of time and irrelevant in programming.
EDIT: Err, never mind. You're apparently inclined to shit on anything you don't like in most of your comments.
I once felt that way. Then I learned Elixir, and the actor model is my favorite thing of all time. It has amazing abstractions for dealing with concurrency.
I wrote a PhD on method_missing, and I still don’t really see how Ruby is message passing rather than just normal method calls.
As an implementer, as you of course know from the amazing optimizations Truffle Ruby does, there are certainly lots of special cases where you can aggressively decide from an implementation point of view that you know how the recipient will act. So in practical terms a lot of code that uses systems that conceptually can do message passing will in practice in many - or even most - cases act exactly the same as a pure method-calling system.
But the conceptual difference is that in a message passing system, you can not do so for the general case before execution starts, because there is the chance that the treatment of messages change dynamically at runtime. E.g. the "fun" case of a program that eval()'s a user supplied string that redefines a method, to take the extreme case. "pry" and "irb" are good examples...
To me at least, message passing and "extreme late-binding of all things" relate to systems that conceptually allow deferring the decision of which piece of code will get called until the moment a message is passed, whether or not a specific instance actually makes use of that flexibility, while a "method call" implies that you can know in the general case by static analysis which method will be invoked, or at the very least which of a small, constrained set of methods will be invoked.
Of course you can always emulate one with the other, so the boundaries gets fuzzy.
EDIT: Put another way: You can implement message passing in any system by defining a send(object, ... args) function that implements dynamic lookup and allows the program to modify the lookup at runtime. One step up is to implement a "send(...args)" method on a class in a C++/Java style OO system. So any system can have message passing. But the difference in whether or not we consider a system message passing is largely a question of whether or not the system provides syntactic sugar to make that easy and whether or not it is considered idiomatic use.
Re: method_missing, I supoose it's not as easy to understand as I thought, which is to say if I understood it better, I could explain it better. The best I can do is this...
1. A typical method call in Java means "I pass a message (args) to a method on some object, and block until I get a response", which usually means building of up stack of blocking calls waiting for a response". The message passing is sending the message, which in Java is tightly coupled to receiving the response.
2. In Erlang, you might have a process (think of it as an object for this example [1]), that sends a message to another process. The sending process doesn't need to wait for a response. Erlang does have function calls that feel pretty much exactly like method calls in Java, and then there is message passing between processes, and that separation makes the differences more clear to me, which is not to say I understand things correctly.
3. method_missing in Ruby is kind of like sending a letter in the mail to Santa Clause. There is no physical address for Santa (aka an undefinded function), but someone at the post office looks at the intended destination (SantaClause.mailing_address) of the North Pole and message itself ("I have been good, and want a Nitendo Swith for Xmas"), and determines how to respond (method_missing) to the message.
I do not know if this explanation is any good, so please be kind :) Doing my best!
[1] this article might explain things better than I can... https://blog.noredink.com/post/142689001488/the-most-object-...
Then you have a broad "polymorphism" that guarantees all the parts of the system just take messages and do things. Obviously, in practice that could be implemented by function call but having the confidence that programming units are only going to receive messages makes it easier to think about the system - hopefully.
I was thinking recently on the idea of “sending a message” versus that of “calling
a method”. Those familiar with the capabilities of the Objective-C runtime understand
that there is a difference.
Most compiled languages refer to methods and functions internally as offsets. The
offset indicates that if one were to move a certain number of spaces in memory
from a designated starting point, then we would find the beginning of the
instructions that correspond to the desired function.
This has the advantage of being very fast. The starting point is known at compile-
time, and so to find the appropriate section of memory takes just a couple of
instructions. However, it has the disadvantage of being inflexible. This
is where most compiled languages differ from Objective-C
The Objective-C runtime maintains a list of all the methods and functions it knows
about. The list has two components per entry: the name of the method (known as
the method’s “selector”) and the location of the method in memory.
When an object attempts to “call a method”, it is really behaving quite differently.
When the code was compiled, the compiler translated the code [anObject
doMethod:aParameter]; into objc_msgSend(anObject, @selector(doMethod:),
aParameter); (basically. The actual behavior is slightly more complex but is all
explained in the documentation).
The objc_msgSend() function does a dynamic lookup. It knows the name of the method
it’s supposed to find (@selector(doMethod:)), and so it goes and looks up in its big
list where it can find what it’s supposed to do next.
This allows us to do some really interesting things. For example, we can modify this
list whenever we want. We can swap out values of the list, so that instead of
selector A executing the code for A, selector A might instead point to the code
for B. The runtime allows us to do some very powerful stuff.
The disadvantage is that this is (as you might imagine) slightly slower than directly
jumping to the appropriate section in code. However, the difference is
minute. Each message send takes just a couple nanoseconds longer than a regular
method call.
---[0]: http://davedelong.tumblr.com/post/58428190187/an-observation...
Because a lot of people (including me) have a Simula based view of "object oriented", we tend to think of objects as data structures with functions attached to them. Alan Kay had a different view, as far as I can tell. He viewed objects as being a collection of abilities. You could invoke these abilities by sending the object a "message". How you send that message is irrelevant. The important thing is that the object is not a collection of data, but rather the object contains the program state necessary to provide the ability (and nothing more). One of the things he talks about (I can't remember if he does in this specific email exchange, though) is the idea that once the data is inside the object, you can't actually access it any more. It becomes a detail that the programmer doesn't have to worry about.
As an example, it's tempting to look at a point on a 2D plane as a tuple containing an X coordinate and a Y coordinate. However, let's forget about the data and instead think about the actions that you might want to do with a point. You might want to construct a vector from it (normalised from the point 0,0). You might want to translate it (by giving it a vector). You might want to rotate it around another point at a certain angle, etc, etc. From the outside perspective there is no reason to access the X and Y coordinates. We don't have to care about what a point object contains -- we only have to care about what messages it responds to.
But how is that really different that having a struct with X and Y and a bunch of functions attached to it? I thought pretty hard about this and one of the things I thought about was what if we approached this in a more functional, rather than imperative fashion. Alan Kay was, after all, coming from an FP background.
For example, let's say that we have a collections of points and we want to "indent" them by pushing them all to the right based on the X coordinate of an existing point. Our "normal" approach would be to get the X coordinate of the existing point and then add that value to the X coordinate of all the other points.
Instead, though, what if we had a method on Point that accepted a function. The method would call the function and furnish the x coordinate (we could call it "with_x", perhaps). Now we can use that method to construct a Vector, setting the x coordinate of the vector to the x coordinate of the point. We could then map over our collection of points and translate using the vector.
The difference is subtle, but I think it's important. With a struct, we essentially export program state out of the Point object. With this more functional approach, we run the function within the Point itself. In other words, we are asking the Point to construct the Vector itself, by sending it a message.
I think this is what Alan Kay meant when he talks about message passing. It's not about the mechanism of the passing of messages, it's about where the computation is performed as a result of that message passing. In our example, the X coordinate of the point never "leaks out" of the point. We can use it in the context of the point, but we can just grab it in the middle of some other computation.
Is that distinction important? I've tried to write some non-trivial code in that style and I really liked how it came out. I'm not sure if it's "better" than doing it another way, though. I would have to spend considerably more time with that style of programming to say for sure.
Now method calls and function calls: to me they're the same thing. After over 20 years of Java, I still stumble over that terminology. They're function calls, OK?
Of course depending on the language a keyword like "function" need not create something that always returns the same result for the same arguments. JavaScript for instance.
A method is "associated with" an object and has access to the private state of that object. Of course depending on the language that might differ but we are here talking about what is an "OO language".
Another often overlooked difference between the two is that many discussions compare a function with immutable arguments, with a method operating an a mutable object. And then the method is said to be not a function because of the object's mutability. But there is no need for the object to be mutable or for any arguments to be immutable.
Perhaps the most substantial difference is a method's dispatch on the object type. But if we're going with Kay's definition, then that is not an essential part of OO, and again, you can imagine dispatch in function invocation too.
To summarize: When I start taking apart the different aspects of function invocation and method invocation, I have a very hard time seeing the difference.
This illustrates the difficulty of differentiating these things, as pretty much everything we do is "just" syntactic sugar. But of course that syntactic sugar often has substantial impact on how we think.
Nobody's definitions here seem consistent or applicable to implemented systems! Crazy how much a well-known idea can be so hard to pin down.
Also, isn’t it odd that your replies all say something different what message passing actually is?
http://www.nextop.de/NeXTstep_3.3_Developer_Documentation/Ge...
The difference lies in the approach to software design[1].
[1] If I could be more specific, Mr. Kay would have been so 30+ years ago.
A method call is "done to" an object. The caller decides which code is invoked. It does so with information about the object or its class, but ultimately the object has no direct say.
Message passing on the other hand conceptually grants the object the role of deciding what happens to a message.
Message passing is often implemented in terms of method calls for the simple cases, but an object has the ability to dynamically define how a message should be handled based on state that may be known to the object but not the caller.
A typical Ruby example (and Ruby is a good example - it "stole" most of its object model from Smalltalk) is Ruby ORMs like Sequel (same applies to most Ruby ORMs) that dynamically define accessors for columns etc when connecting to a database and dynamically querying the database for the schema.
In Ruby, method calls is a veneer - you can define a method that looks like it's just like a C++ method call, decided at the callsite, but at runtime the receiving object can be redefined at any point, up to and including changing which methods the object responds to on an object-by-object basis, or defining "method_missing" so that what looks like method calls are entirely dynamic.
> It it supposed to be remotable? So are method calls.
I'd say there is a difference in degree of complexity in remoting. In a message passing system you typically do not need to have any kind of definition of the interface available client side. E.g. Drb for Ruby lets you expose any Ruby code over a network connection this way.
E.g. here is a real example, using the "pry" Ruby REPL. Server side:
$ pry
[1] pry(main)> require 'drb/drb'
=> true
[2] pry(main)> drb = DRb.start_service("druby://localhost:8787", self); nil
=> nil
[3] pry(main)> def hello; puts "Hello world"; end
=> :hello
[4] pry(main)> Hello world
[4] pry(main)>
[5] pry(main)> def hello; "Hello world"; end
=> :hello
The above exposes "self" which is this case is the "main" object of that Ruby instance, but you can pass it any object you want, and all public instance methods on that object will then be remoted, and any objects returned from those methods will be remoted dynamically as needed.It then defines the "hello" method on main after the server has been set up. Doesn't matter when the methods are defined, as long as they're there when you call them.
The redefinition of "hello" in [5] happens after I've called hello the first time from the client (the "Hello World" in [4] was triggered by the client. The redefined version returns the string to the client instead of printing it on the server:
$ pry
[1] pry(main)> require 'drb/drb'
=> true
[2] pry(main)> client = DRbObject.new_with_uri("druby://localhost:8787")
=> #<DRb::DRbObject:0x000055828909f220 @ref=nil, @uri="druby://localhost:8787">
[3] pry(main)> client.hello
=> nil
# Here we redefine "hello" on the server.
[4] pry(main)> client.hello
=> "Hello world"
Is it supposed to be asynchronous? It never seems to be so in practice. It it supposed to be remotable? So are method calls. Are languages like Objective C and Ruby message passing to some extent? Why is that message passing rather than a method call? What's the essential difference? Is Java message passing?