70

I have read that in OOP, we think of objects as "sending messages to each other", for example if we did car1.stop(), we say that "we sent the message stop() to the car1 object".

But what benefit do we get by thinking of objects as "sending messages to each other?" What I mean is, let's say that we thought of car1.stop() as "calling the method stop() on the car1 object." What's wrong with thinking of it like this?

14
  • 12
    Does this answer your question? Alan Kay: "The Big Idea is Messaging" – gnat Oct 14 '20 at 11:56
  • 2
    This question also has some good answers. – IMSoP Oct 14 '20 at 13:07
  • 3
    one wonders given that he commented, which of the answers to that question if any alan kay upvoted – Ewan Oct 14 '20 at 13:22
  • 9
    In the context of mainstream OOP (see C++, Java, C#, etc.), as you already seem to suspect, there is literally no difference. This is why the answers that attempt to come up with a difference end up saying subjective things like "it evokes this image" or "it avoids micromanaging". Because, at an objective level, there is no actual difference -- it's just a function call with a different syntax. – Theodoros Chatzigiannakis Oct 15 '20 at 0:06
  • 2
    This has to do with the history of OOP. The message was conceptualized as an abstraction of a function call - e.g., some token, along with some parameters. An object was something that could respond to this message by calling an actual function. Note that this automatically gives you polymorphism, since many different objects can respond to the same message. So it's not just a different syntax for a function call, it's inherently polymorphic. Modern statically typed languages encode that behavior as inheritance of public members, and the message passing itself sort of happens under the hood – Filip Milovanović Oct 17 '20 at 3:43

11 Answers 11

77

It avoids micromanaging.

If I tell you to stop in an OO way I haven't called your stop procedure, or your stop function, or your stop method. When I send that stop message I've raised a stop event. One that you are free to handle or not. You don't even have to respond. Now sure, you might use a stop method to handle that, but that's your problem.

This avoids micromanaging because I don't have to deal with how you respond to being told to stop. I don't have to think, "OK I told him to stop, now what's he going to do? If he ignores me then I'll do this, if he has a problem then I'll do that, if he stops then I'll do this next thing". No, that's micromanaging. If anything needs to be told what happened when you got told to stop it's better to let you decide who to tell. It gives me fewer things to think about. It gives you more freedom to control your stop response.

This keeps a very low form of coupling between objects. Lower even than typical1 functional programming. Functional programming does composition beautifully. Pure functions make reasoning simple. But it locks you down to sending the response back to the caller. It has nowhere else to go. That couples caller to callee. Messages, however, can go where they've been configured to go without worrying what becomes of them. It's not as straightforward but it's another detail avoided.

Another benefit is minimizing data movement. Functional programming has been called "data in, data out". OOP wraps data in a "message in, message out" system. The messages can be very lightweight compared to the data.

I'm contrasting OOP with Functional here but that shouldn't be taken to mean you exclusively use one or the other. Many of functional programmings principles can be used while using OOP. Prefer immutable objects. Be disciplined with side effects. Etc.

OOP messaging is a powerful way to model. It inherently respects encapsulation. I don't look inside you. I don't ask you about your privates. I tell you what I want done and you decide what, if anything, to do about it. Once I tell you, I don't have to hover over you and manage what you do. I just let you do it. Whatever it is. If I ever need to know more I'm sure someone will tell me.

Messaging is sometimes implemented by using methods as the messages but that's just one way to do it. It could be text messages, packets, tweets, emails, etc. The methods are not what makes it OOP. It's how you use them.

Here's the rub. Just because you're using an “OOP language” that has methods doesn't mean every method is a genuine OOP message. No language perfectly enforces this. Your programming team has to enforce this. Depending on the design, a method may conform to requirements of a OOP message. If you're lucky your core packages will follow this well. I've never worked on a project where OOP was 100% enforced or a functional project where everything was pure. But the better projects will find some way to at least signal clearly where the ideals are followed and where they have been compromised. This is important because it impacts the readability of the code. It's good to quickly know if you're looking at a true OOP message, a pure function, or some other monster.


Joel has blessed us with this awesome comment:

1. Regarding Functional Programming only returning to the caller, I would suggest looking into the technique continuation passing style combined with tail call optimization. "When you're done here, talk to this other guy. I will show myself out." – Joel Harmon

This is all true. But if the caller is saying "talk to this other guy" the caller is still dealing with knowing where to send the response. To put functional programming coupling on par with OOP (that configures output ports in constructors) pass "this other guy" into the enclosing scope of a closure. That way the caller neither knows nor cares where the result goes.

18
  • 15
    Regarding Functional Programming only returning to the caller, I would suggest looking into the technique continuation passing style combined with tail call optimization. "When you're done here, talk to this other guy. I will show myself out." – Joel Harmon Oct 14 '20 at 16:26
  • 1
    @LuisVasconcellos sure. You could call it a message with a side effect but that names taken. But keep in mind it isn’t simply a return that takes away from message “purity”. Even exceptions can make you couple the caller to the callee. A good message is fire and forget. But of course this is simply an ideal. If you’re lucky you can carve out a place in your code base where they dominate. But such a place usually hides from the real world behind boring structural code that does noting interesting and follows few ideals. – candied_orange Oct 14 '20 at 17:41
  • 3
    I don't understand this (paragraph 3). If you tell something to stop then you definitely do care about whether it's stopped! car.stop(); pedestrian.crossRoad(); <- what do you think happens if the car doesn't stop? What would your liability be if you designed a system where the car can ignore your request? (you might say we should check car.isStopped, but of course it returns true, because it would be "micro-managing" if we cared whether it told the truth) – user253751 Oct 15 '20 at 11:15
  • 2
    @user253751: One might issue a blanket "stop" order to a variety of objects without caring whether they were capable of moving in the first place. A lamppost might be able to give a definite "yes" to the question "Will you remain stationary unless or until I say you can move", but have no idea how to answer the question "Has anyone 'stopped' you", since the lamppost should have no reason to know or care about such things. – supercat Oct 15 '20 at 19:44
  • 2
    @Michael if you take this down to the foundational level, how do you ensure that messages are sent reliably? you have to get some kind of response back. if you decouple the response from the reply then you have extra work to correlate the two. this is perhaps why messages are almost universally implemented as function calls, because i think you can't really totally decouple messaging as it was intended without reliability suffering. – Michael Oct 16 '20 at 19:02
30

None.

Not sure where you are reading this idea of objects sending messages, but if it is the same as this question, So what *did* Alan Kay really mean by the term "object-oriented"?, then it's pretty clear that the inventor of the term Object Orientated is talking about passing messages in a way that today's OOP languages just don't do.

So it may be useful if you are programming in Smalltalk, or writing event driven code, but not if you are talking about today's accepted meaning of Object Orientated Programming, which has methods - not messages.

If you call car.stop() you aren't sending the stop message to car and carrying on with your life, maybe getting a message back at a later date. You are "going to" the code in the stop method, running each statement in turn and returning a result back to where you left off.

21
  • 14
    Message-passing doesn't have to mean asynchronous message-passing. It's still reasonable to think about a program as passing messages even if it waits for a message to be passed back, or to write a program that conceptually works with events even though it runs synchronously on a single thread. – IMSoP Oct 14 '20 at 16:00
  • 8
    I don't get your point. The question isn't about how things actually execute at runtime. It's about the mental model we use when designing things. – JimmyJames Oct 14 '20 at 18:16
  • 18
    I'm saying its not a helpful way of thinking about (todays) OOP. People say it is because the inventor of OOP said it. But the OOP he was talking about is not the OOP we have today. – Ewan Oct 14 '20 at 18:21
  • 2
    Its confusing, you are better off thinking about it as the OP suggests "calling the method stop() of the car1 object", – Ewan Oct 14 '20 at 18:23
  • 5
    Oop in smalltalk message passing is not async. I can't think of an OOP that was async. You can write in an async way with fiddling but the default is execute and return the result – mmmmmm Oct 14 '20 at 21:56
16

The answer may be quite simple: "Sending a message", unlike "calling a method", is using a language which is understood outside of the software domain. When explaining abstract concepts it is always good to use words from another domain which the audience has an understanding for.

3
  • Yes, I also believe this to be the one reason. OO's ambition has always been to offer a universal information modeling language that could be spoken across domains. So users can get the same idea as implementers early in the development cycle. The term "call" paired with whatever a subroutine is named in a particular language is both too technical and too technology-specific. – Martin Maat Oct 15 '20 at 20:21
  • Alan Kay explicitly referred to the Internet (well, what would later become the Internet), when he coined that term. He explicitly wanted to evoke the idea of "objects as little computers" on the Internet. Just like computers on the Internet, the only thing you can do, is send it a message. You can't access the code on the remote computer (methods), you cannot access its RAM (instance variables), you can't tell whether the response is coming from the remote computer directly, from a proxy, whether it was re-routed multiple times. That metaphor is fundamental to OO as envisioned by Kay. – Jörg W Mittag Oct 17 '20 at 12:40
  • I would argue it is exactly the opposite: he used the terms "message" and "send" precisely because of the way that they are understood by programmers, because message sending in OO is fundamentally different from subroutine calling. Note that Smalltalk uses other networking related metaphors as well, e.g. "protocol" instead of "type" / "contract". – Jörg W Mittag Oct 17 '20 at 12:41
7

In addition to the points that candied_orange made, this concept is highly useful in for multi-threaded and concurrent systems. In such systems, you can't depend on the notion that when you call a method that it will be the next thing that happen in your program or that the calls will even execute in the order they were called. Doing so will lead to race conditions and other problems.

It's helpful instead to think about method calls as a message to another object that it will receive sometime later, perhaps after some other message has been processed. This model can help produce robust systems without relying too heavily on locks which can create contention and hobble performance.

0
4

I think this may mainly be due to the history of object-oriented programming. Some of the early OO languages had syntax that explicitly used messages.

Smalltalk described its operations as messages, using a syntax like:

car1 stop

And in Lisp Machine Lisp Flavors you would write

(send car1 ':STOP)

As you can see here, the message argument is an expression (since it needs to be quoted above), so you can abstract it away in a variable.

(let ((msg (if (some-condition) ':START ':STOP)))
  (send car1 msg))

Of course, these are isomorphic to function calling, and CLOS dropped the explicit message passing syntax in favor of generic functions with no loss of functionality. And you can accomplish dynamic message passing with first-class function objects.

But many programmers still like to think of this as message passing, because one of the principles of OOP is that each class takes responsibility for how it implements actions. Calling a method is considered to be telling the class of the object to take an action, and that seems analogous to passing a message to an autonomous actor ("actors" and "messages" are also common in many asynchronous programming models).

3
  • 1
    This answer fails to mention that Alan Kay - who coined "Object Oriented" and who argues for message passing - worked in the development of Smalltalk. Also, this answer does not argue much about why we use the metaphor beyond "historical reasons". I did not down vote. – Theraot Oct 15 '20 at 16:04
  • @Theraot My hypothesis is that if the original OO designers hadn't used that metaphor, we wouldn't still be using it now when we use function-call notation. – Barmar Oct 15 '20 at 16:08
  • 1
    I don't see how carl stop syntax "explicitly used messages" whilst carl.stop() doesn't. Both can be understood as either a lookup ("Which stop should I run? The carl stop") or as sending a command/message ("Carl. Stop!") – Warbo Oct 17 '20 at 0:35
4

I think the 5 answers have missed the point. Let's admit one thing to start off. Whether it is called a message or a function it is just code that gets executed.

Function and subroutine had long been used before method was coined. So why was a new word needed? Because both function and subroutine call very particular pieces of code. Thinking of the call as a message allows a new paradigm.

With inheritance you don't know where the method is actually implemented. Let's say that in this case that car is a child of vehicle and it is vehicle that first implements the stop message. Thus car doesn't have to implement a new message. Car inherits the code to implement the stop message from vehicle.

However let's say that we also have a amphibious_vehicle which is also a child of vehicle. Now vehicle's stop method is assuming braking on dry land by sending a message break(0) to itself. Brakes work on land, but don't work on water. So while in the water something different is needed for amphibious_vehicle.

But this means that all vehicles will understand stop() regardless of how the particular child of vehicle may override vehicle's code for the method.

2

In the olden days, sending a message in C++ or Objective-C was indeed just a glorified function call. Admittedly the call was dynamic, so you didn’t have to figure out which function body to call, just the method name, but under the hood it was just a function call.

That has been changing in the last years, at least where I work. There are so many things where you cannot just call something and wait for it to happen. Just a plain URL request: It can happen in a few milliseconds, but even that is too slow to waste the time waiting for it. But it might time out after 60 seconds. It might detect there is no WiFi and ask the user to turn WiFi on. It might run into errors that can be fixed by retrying. This is all so complicated, you can’t just make it a function call and wait.

Now the message metaphor works really well. Instead of ordering “download that URL” and waiting for the result, you send a message “I’d like that URL, please”. You continue what you were doing, and eventually someone will send a message back to you, either reporting the contents of the URL or reporting an error. And at that point your whole situation might have changed; you wanted to download an image and display it, but meanwhile the user switched to a different page! With a plain call, that wouldn’t have been possible.

5
  • 3
    Objective c never was a simple function call. It is fully dynamic. The object you send to can change on the fly. It uses messages – mmmmmm Oct 14 '20 at 21:58
  • 1
    @mmmmmm It does break apart at one important difference - even in Objective-C, "sending a message" still allowed you to "listen to response". Indeed, there is no built-in way to not listen to the response - at best, you can ignore it. – Luaan Oct 15 '20 at 10:35
  • What I like in this perspective is that it allows "message" to capture a bigger interaction from the "method call", making it actually distinct. Once we have asynchronous code, the objects action taken when "passed a message" may well spread beyond the (synchronous, in my mind) method execution. OTOH, I think in all practical applications I'd rather want to know whether the action would be synchronous or not - and so I still prefer to think about the method call, which either does an action or starts an action. – Frax Oct 16 '20 at 10:41
  • 1
    @mmmmmm I appreciate that you totally agree with me, that the calls in Objective-C are dynamic. I've never in my 33 years of using Objective-C say the object that I'm sending to change. – gnasher729 Oct 16 '20 at 16:13
  • @gnasher729 I did not use it myself but see poseAsClass (depreciated OSX 10.5) and swizzling of methods. Some people do use that sort of thing. – mmmmmm Oct 17 '20 at 15:04
2

"Messages" Metaphor

The "messages" metaphor helps you in creating well-designed classes, encapsulating their internals. But it's a metaphor, one specific way of viewing object-oriented software.

The metaphor of a lot of employees (instances) doing different jobs (classes), and communicating by sending messages and waiting for answers more naturally tends to create a structure where the message types are well designed and grouped according to the jobs of the recipients.

In this metaphor, for every message type, you have to decide on some very useful aspects:

  • What is the content of the message? What does the recipient really need to see in the message to create an answer? This translates to method name and parameters.
  • Who will be the recipient of this message (who will be responsible for answering)? This translates to classes and instances.
  • Which notes will the recipient need to maintain over various messages to be able to answer adequately? This translates to instance fields.
  • Do all the steps necessary for processing a message fall into the responsibility of the recipient? Or are there steps that should be delegated to someone else? This translates to implementing the method either locally or by sending subordinate messages to other instances.

"Method-calling" View

Of course, method calling is an absolutely valid view of object-oriented software. That's what happens at the core of object-oriented software. And of course you can ask all the above questions with a method-calling terminology as well.

But the method-calling view can more easily put the method into focus with its procedural content and ignore the "responsibility" aspect.

Have you ever found yourself thinking along the line:

Oh, I need to do X [e.g. print some nicely-formatted currency amount] here. Wait, I've done this in the Y [e.g. BankAccount] class, so let's just create a Y [BankAccount] instance, have it print the value, and then forget about it.

From a procedural view, that's fine, but really bad object-oriented design.

The "messages" metaphor and the questions that it naturally brings along, help me not to fall into this trap.

1

Here are some linguistic reasons.

Firstly.

When there are no parameters, there isn't a lot of daylight between

  • "Send object Z message X()" and
  • "Call method X of object Z".

But, when you have arguments (e.g. Z.X(Y)), we can think of X(Y) as being the message. To say "Call method X(Y) of method Z" is wrong because X(Y) is not a method. We have a choice between.

  • "Send object Z message X(Y)" and
  • "Call method X of object Z with argument Y.

I prefer the former because it lets me talk about X(Y) as one linguistic unit, which I call the "message".

Secondly.

"X" is not a method, it's a name. In a non-OO language, this distinction isn't that important. If a Fortran programmer says "Call subroutine X", it's clear that they mean "Call the subroutine whose name is 'X'". That's the whole point of names; we can use them in place of a named thing, because each name should identify one and only one thing (in each context). But, in an OO language, there isn't typically a single method with name "X"; "X" might be the name of an abstract method and also of various concrete methods; so saying "Call the method whose name is 'X'" doesn't make sense and nor does "Call method X" and nor does "Call method X with argument Y." I have to say the "of object Z" part; and that is sometimes inconvenient.

Thirdly

Saying "Call method X of object Z" makes it seem like the only role of Z is to provide a context in which to interpret the name "X"; and once it's figured out which method named X is to be called, the machine just goes ahead a calls it as if it were any other subroutine. But, that's not what's going on.(*) Z is used not only to find the method, but also it also plays the role of the recipient. So to be precise we need to say something like

  • "Call method X of object Z with argument Y and with object Z as the recipient" rather than the more metaphorical and shorter
  • "Send message X(Y) to object Z".

As an instructor, I worry that the message sending metaphor can easily lead students astray. I use it, but I try to help the students understand that underneath it all, whether you call them "message sends" or "method calls", it really is just a kind of subroutine call.


(*) In some languages, an expression Z.X(Y) might be considered equivalent to (Z.X)(Y), where Z.X is an expression that evaluates to a closure that can be applied like any other closure. But this is not how method calls (or message sends) in most OO languages work.

0

We understand messages to be:

  • Slow... They take time, and care to draft
  • Sent through some indistinct mail box that simply accepts messages
    • The mail box does not respond to us with any indication of success/failure
  • Hands off - Someone else is responsible for directing that message to its destination/s, or there are other process that can affect a directly delivered message (say placed on a desk, knocked off by a cleaner).
  • Undelivered potentially. Its not like the postman/horse rider/office worker is a 100% reliable delivery service. Accidents happen, addresses are misunderstood, etc...
  • Unread by the addressed individual. It might be read by their replacement, family member, colleague, or executor of the estate in a tragic case.
  • Potentially unintelligible by the recipient. They might not know how to make sense of it, or even attempt to read it using other techniques (such as reading it through a mirror, or reading the First Capitalised letter of each paragraph).
  • Unresponded to. The recipient does not have to choose to engage. Imagine if everyone responded to their junk mail...
  • Slowly responded to. As the recipient takes their good time in drafting a reply, gathering information, considering it, and forming it into a suitable response.
  • One of many things a recipient is dealing with. Its not as if your message is top of the pile, and even if it is it might be disregarded or delayed in favour of other activities.

And we know that about message passing from lived experience.

Functional Calling though evokes directly talking to someone face to face: Instantaneous, Direct, Real time, no wait, Two-way dialogue, with every side-effect apparent to the observant.

2
  • "We understand messages to be" No, we don't. What you are describing seems to be YOUR experience with something you THINK is "object-oriented." – Jan Steinman Nov 4 '20 at 19:12
  • @JanSteinman I was discussing why one might choose to discuss it as message passing vs say a function call. I'm not actually discussing Object-Orientation here. I'm interested in how you perceive passing messages physically in the real world, and what your opinion is on the use of that experience in discussing message passing in programming languages. – Kain0_0 Nov 4 '20 at 23:45
0

The most commonly used OO languages we have today, things such as C++, Java, C#, do not do what Alan Kay originally thought of:

Alan Kay says...

I was too blythe about the term back in the 60s and should have chosen something like "message oriented" – Alan Kay Jun 8 '11 at 16:27

by which he means actual data messages, not method calls.

So in the context of the above languages, not only is there no benefit in thinking of objects as "sending messages to each other", it is actually dangerous, especially in multi-threaded and parallel systems, due to the difference between sending an actual message, and performing a method call. Call stacks do not cross threads, so cannot correctly model call chains in those circumstances.

Carl Hewitt defined the Actor Model, which introduces actual message passing as a work-around in those languages which do not inherently support actual messages.

Not the answer you're looking for? Browse other questions tagged or ask your own question.