73

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?

dsomel21
  • 161
  • 4
Christopher
  • 2,029
  • 3
  • 13
  • 16
  • 12
    Does this answer your question? [Alan Kay: "The Big Idea is Messaging"](https://softwareengineering.stackexchange.com/questions/264697/alan-kay-the-big-idea-is-messaging) – gnat Oct 14 '20 at 11:56
  • 2
    [This question](https://softwareengineering.stackexchange.com/q/140602/96713) 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
  • 1
    @TheodorosChatzigiannakis worth noting that isn't the case in all languages, e.g. erlang. – Jared Smith Oct 15 '20 at 11:17
  • 1
    A smart traffic light of the future will not send a `stop()` message. It will send a `turning amber against Main Street; red light in 6 seconds` message. A car two seconds from the intersection should completely ignore the message. A car a bit over five seconds from the intersection might want to speed up. A car eight seconds from the intersection should slow down and come to a stop. – David Hammen Oct 15 '20 at 21:15
  • @Ewan Where did Alan Kay comment in this thread? – Michael Oct 16 '20 at 18:47
  • "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" – Ewan Oct 16 '20 at 21:01
  • theres another good quote in one of the answers : "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. **There are possibly other systems in which this is possible, but I'm not aware of them**." – Ewan Oct 16 '20 at 21:08
  • 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 03:43
  • In other words, when you write `car1.stop()`, the `stop` doesn't refer directly to an actual function; there's a level of indirection that resolves to a concrete function call depending on the actual type of the object. – Filip Milovanović Oct 17 '20 at 03:46
  • I'm having some difficulty understanding the thrust of the question. You're asking for the compelling difference between "sending a message" and "calling a method"; suppose I did not understand what "calling a method" was. Could you explain it to me? How would you do so? Understanding how you think about methods might help clarify the question. – Eric Lippert Oct 27 '20 at 18:58
  • "There are possibly other systems in which this is possible, but I'm not aware of them." Ruby. Objective-C. Swift (sort-of). – Jan Steinman Nov 04 '20 at 19:17
  • Pithy answer: “You get to avoid getters and setters” – gntskn Nov 13 '20 at 16:31

11 Answers11

79

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.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • Interesting comment about not enforcing "pureness" in FP to 100%. Because if you don't, you can basically throw away the whole thing. I mean in FP basically everything is *deferred*. You normally work inside some deferring monad, usually some form of `IO` to host all the side-effects. So if you have just a single function that is not pure it will cause a side-effect immediately even though potentially nothing else yet has been executed. Maybe if you are _extremely_ careful you could get away with it... But why would you even try? – Robert Bräutigam Oct 14 '20 at 16:04
  • 16
    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
  • Would it then make sense to think a method that returns anything other than void is exposing more of its internal state than a method that just returns void, and thus the use of this first method could be thought as a imperfect/flawed example of the message passing mechanism? – Luis Vasconcellos Oct 14 '20 at 17:19
  • 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
  • 1
    @LuisVasconcellos "make sense to think a method that returns anything other than void is exposing more of its internal state than a method that just returns void" I'm not sure that this is totally sensible. It could be true that the method's return value is inherently related to it state. Naïve 'getters' come to mind. But in general, you don't know that the object has any state (aside from say, it's class type) at all and you definitely can't, in general, derive that state from the return value. – JimmyJames Oct 14 '20 at 18:12
  • @Jimmyjames true. Even sending a message can exposes internal state. Abstracting internal state properly requires more than a simple structural analysis. In the end it’s about minimizing what you break when you redesign your state. – candied_orange Oct 14 '20 at 18:17
  • 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
  • @user253751 traffic lights tell you to stop all the time without checking up on you. Doesn’t mean the traffic cop isn’t checking up on you. Just because it’s important doesn’t mean micromanaging is the answer. – candied_orange Oct 15 '20 at 11:26
  • @candied_orange and the pedestrian looks at the car to see whether it's stopped. It would be micro-managing if `car.isStopped` was expected to return a correct result, though, right? – user253751 Oct 15 '20 at 11:34
  • @user253751 nope. The car tells the pedestrian “I’m a cop car running my siren so yes I’m heading thru this red light at 20 miles an hour.” There’s no need to ask. – candied_orange Oct 15 '20 at 11:39
  • @candied_orange does the car tell the pedestrian "I've stopped"? – user253751 Oct 15 '20 at 11:48
  • 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
  • @supercat I thought there were no questions? How does the pedestrian learn that the lamppost won't move without asking it? – user253751 Oct 16 '20 at 09:38
  • it sounds me like what is being said is that message passing degrades the computing experience, where everything can proceed and be checked exactly in a precise fashion, to a real world experience, where sometimes things go wrong and hopefully we have a good system in place to deal with that. all for the benefit that if we change our minds later it will be easier to make changes to the system. – Michael Oct 16 '20 at 18:45
  • 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
  • @user253751: In a message-passing architecture, messages may include "Send me a "true" or "false" message based upon some condition" or "Either send me a message if some condition is true, or do nothing". In many object-oriented frameworks, the concept of a message to which an immediate reply is required can be handled efficiently without the reply having to be routed back to the requestor. – supercat Oct 17 '20 at 16:46
  • 1
    "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." - This is one of the best explanations I ever saw about encapsulation, responsibilities and decoupling. – Rubidium 37 Nov 04 '20 at 11:30
  • @candied_orange Also the traffic cop isn't allowed to check whether the car stopped for the same reason the traffic light isn't. – user253751 Nov 13 '20 at 17:28
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.

Andreas Rejbrand
  • 1,791
  • 1
  • 10
  • 7
Ewan
  • 70,664
  • 5
  • 76
  • 161
  • https://docs.microsoft.com/en-us/dotnet/csharp/async – JimmyJames Oct 14 '20 at 15:11
  • you know that behind the scenes async is a loop and an if block? – Ewan Oct 14 '20 at 15:13
  • 15
    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
  • sure but neither of those things are related to OOP. "message passing" in OOP is related to an outdated idea of what OOP is – Ewan Oct 14 '20 at 16:33
  • "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." That's literally what async calls are doing. – JimmyJames Oct 14 '20 at 17:59
  • well yes and no, but also async != OOP and even car.stopAsync() has defined parameters, I cant send any message and the return "message" has a defined type and point of return. In an event driven paradigm i would do Mediator.Send({"car23","applybrakes"} and Mediator.Listen("me","getthrownintomyseatbeat") – Ewan Oct 14 '20 at 18:05
  • 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
  • 19
    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
  • Some of the other answers demonstrate why the **mental model** or **metaphor** of "message passing" is still a useful one, even if it's not fully embodied by the languages we're using. Ultimately, most of the **implementation** of OOP is just funky ways of calling subroutines, so even "method" is an unnecessary distinction if you're thinking purely mechanically. – IMSoP Oct 16 '20 at 08:55
  • 1
    the question is whether its a good way of thinking about OOP. Other answer are really talking about supposed benefits of event driven architecture, where yes, obviously it is literally passing messages – Ewan Oct 16 '20 at 09:07
  • I can see the advantages of both arguments, and perhaps it depends on a person's level. Entry level can be taught to see it as a message, but that's not what's going on when you get into more complex scenarios. Message based aka Event Driven are typically fire-and-forget, and the sender truly doesn't care what happens. It creates a break between processing contexts. In terms of the OP's example, the caller very much cares because it has to wait for the `stop` method to complete. If an error is thrown, it may have to handle it. If `stop` returns a value, the caller may have to handle the result – ps2goat Oct 16 '20 at 17:20
  • But from a higher level (not being in the actual complexities of implementation), I can see how thinking of it as a message may be helpful. – ps2goat Oct 16 '20 at 17:22
  • 1
    I guess my thinking is, are you looking for a reason it might be useful, or are you looking at who origionaly said it and why and asking if it still applies? – Ewan Oct 16 '20 at 17:35
  • Objective-C is also implemented as message-passing (and not async), FYI. One consequence is that it becomes quite natural to add message interceptors. – OrangeDog Oct 16 '20 at 22:55
  • I don't know why you think "today's OOP languages" don't follow the message-passing model. Message-passing is essentially synonymous with dynamic dispatch. This is most clear in dynamic/scripting languages, which allow "magic methods" to respond to arbitrary method calls in arbitrary ways; static languages like Java restrict this somewhat, but we can still use subtyping and interfaces to let the receiver decide how a message should be handled; e.g. if we call the `foo` method of an `IFooable` object, the choice of which subroutine actually gets executed depends on the object. – Warbo Oct 17 '20 at 00:28
  • 1
    because Alan Kay says they don't – Ewan Oct 17 '20 at 08:18
  • 2
    Firstly, the question doesn't ask if it's useful to apply *Alan Kay's original definition of message passing* to modern languages, it asks if message passing is a useful metaphor. Secondly, I don't see anywhere in the linked quotes from Alan Kay that says he was talking about messages as "fire and forget", only "extremely late binding"; he doesn't seem to have advocated a purely event-driven approach where messages never had responses. – IMSoP Oct 17 '20 at 11:29
  • async is somewhat irrelvant. sure, if the question is "is this metaphore i randomly made up uselful?" you can always imagine a reason it might be. Thats why I call that out at thevstart of my answer – Ewan Oct 17 '20 at 12:21
  • All software is literally nothing but sequences of ones and zeros. Everything else is to make it easier for people to work with those ones and zeros. – barbecue Oct 17 '20 at 17:46
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.

aax
  • 281
  • 2
  • 2
  • 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.

JimmyJames
  • 24,682
  • 2
  • 50
  • 92
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).

Theraot
  • 8,921
  • 2
  • 25
  • 35
Barmar
  • 324
  • 2
  • 5
  • 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 00: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.

MaxW
  • 149
  • 2
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.

gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • 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.

IMSoP
  • 5,722
  • 1
  • 21
  • 26
Ralf Kleberhoff
  • 5,891
  • 15
  • 19
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.

Theodore Norvell
  • 858
  • 6
  • 10
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.

Kain0_0
  • 15,888
  • 16
  • 37
  • "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 04 '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 04 '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.

Jool
  • 160
  • 6