7 ms·
People say that OO is “meant” to be about message passing all the time, quoting Alan Kay and others. I never found this helpful. No mainstream OO language works
by codeflo 5y ago
People say that OO is “meant” to be about message passing all the time, quoting Alan Kay and others. I never found this helpful. No mainstream OO language works this way, even Smalltalk doesn’t. You can’t design or understand real-world systems(1) with this message-passing metaphor, it will only mislead you.
Objects don’t “do” anything, they are simple passive data structures that are acted on by procedures. A certain set of procedures with privileged call syntax and visibility into the data structure are called its “methods”, that’s all the magic. The sooner you accept this, the better your designs are going to be.
(1) Edit: I meant here that you can't design real-world systems that way in OOP languages. I left that out because I considered it obvious from context, but it seems like it wasn't. I didn't mean to disparage any actual message-passing systems. My claim here is that OOP, as commonly understood and implemented by Alan Kay himself in the form of Smalltalk, is not about message-passing. Erlang is, but that's a different topic.
- DemocracyFTW 5y agoThis! I never understood the why for this 'send-a-message' way of talking when what I do is really call a function that may or may not affect the state (of the object it is being called on or any other object).
- masklinn 5y ago> I never understood the why for this 'send-a-message' way of talking when what I do is really call a function Because you don’t, dynamic dispatch means you don’t know and are not supposed to care how the object handles or responds to the message. That is maybe more flagrant in Self.
- codeflo 5y agoBut you’re describing indirection, not message passing. Is that the source of the confusion? Even C has indirection.
- masklinn 5y ago1. C does not have only indirection. 2. Message passing is an ideal, an ethos, the implementation (or the interface) can get in the way of that. That the messages are implemented as dynamically dispatched function calls… would be an implementation detail. Though with far ranging implications of course (especially in that the response is direct and synchronous rather than an other message as it would be in, say, Erlang). The mention of Self was not innocent, though it does use synchronously dynamic dispatch, a message might invoke a block, or just set or retrieve a value. If you’re asserting that it’s all « calling a function » then you’re basically just reductio at absurdum-ing everything to calling functions which… sure? You can model and reduce everything to lambda calculus.
- codeflo 5y ago> That the messages are implemented as dynamically dispatched function calls… would be an implementation detail. Though with far ranging implications of course It can't both be only an implementation detail and also have far reaching consequences. (It's not an implementation detail.) > You can model and reduce everything to lambda calculus. You actually can't, that's precisely my point. Lambda calculus is inherently "synchronous". You can't capture the full semantics of actual message passing with lambda calculus, you need a process calculus (like pi calculus) for that. (In case you're confused, this has nothing to do with Turing completeness, it's about modeling the behavior of concurrent systems. It's a really interesting subject.) The point is, lambda calculus is sufficient to model OOP method calls, including everything in Smalltalk and Self. It's not sufficient to model the semantics of Erlang messages.
- ProfHewitt 5y agoConcurrent computation is not reducible to the lambda calculus. See https://papers.ssrn.com/abstract=3603021 https://papers.ssrn.com/abstract=3603021
- geofft 5y agoDynamic dispatch just means you're calling a function that happens to call another function. You're still calling a function. I don't think "You don't know the implementation" implies you're not calling a function - otherwise we'd have to say that when you use a shared library like OpenSSL, you don't "call" SSL_CTX_init(), you pass a message to the OpenSSL library, which might be 1.1.0 or 1.1.1 or even BoringSSL. But nobody would say that - it's obviously a function call. (I suppose you could defensibly argue that a system call is message passing, at least on some OSes, but that's about it.)
- codeflo 5y agoYou're absolutely correct, of course. I have no idea why obvious facts are downvoted to hell in this thread.
- 1_player 5y ago> You can’t design or understand real-world systems with this message-passing metaphor, it will only mislead you. Erlang disagrees. BEAM processes and its implementation of a mailbox is probably the purest interpretation of Alan Kay's OOP, and you can indeed design and understand real-world systems using the actor model as implemented in the BEAM VM. In practice it's very simple to grasp, it's concurrent out of the box yet you never have to think outside the single thread.
- codeflo 5y agoI don't disagree that the actor model is useful. I just prefer to call it "the actor model", as you seem to do in your comment as well, instead of confusing it with what 99%+ programmers mean when they say "OOP". (Also, personal opinion: The "purest intepretation" of what Alan Kay had in mind in 1969 is Smalltalk. That's almost an objective fact: if he had something else in mind, he would have done it. I know that he says he had something like Erlang in mind from the very beginning, but to the best of my knowledge, those claims are retroactive.)
- rubyn00bie 5y ago> prefer to call it "the actor model", as you seem to do in your comment as well, instead of confusing it with what 99%+ programmers mean when they say "OOP". You’re totally missing the point. Erlang is more “message passing” than any OOP language I’ve ever used. It honestly just sounds like you’ve never used erlang since you immediately referenced actors.
- codeflo 5y ago> You’re totally missing the point. Erlang is more “message passing” than any OOP language I’ve ever used. My point is that OOP is not about message passing. You say that Erlang is more about message passing than OOP languages are, aren't we exactly in agreement here? Also, it was the post I responded to that referenced actors, I'm not jumping to anything.
- 5y ago
- slver 5y ago> People say that OO is “meant” to be about message passing all the time, quoting Alan Kay and others. I never found this helpful. No mainstream OO language works this way, even Smalltalk doesn’t. It's how they work, however. The fact these messages tend to be synchronous in languages like Java which are mostly about intra-process communication is a matter of optimization. But look at Erlang. That's probably the most pure OOP language we have, and hence why it scales so well the way Kay predicted OOP would. Also all our "web APIs" are in effect OOP. > Objects don’t “do” anything, they are simple passive data structures that are acted on by procedures. Those procedures are an inseparable part of the objects, which is one of the first thing you learn about OOP... > I meant here that you can't design real-world systems that way in OOP languages. You need a relatively small step from OOP to actors. Sure some help from the language runtime would help to keep the model clean. But let's say that mainstream OOP languages take SOME but not ALL principles of OOP and those are the principles they had use for in their context. This is how it should be. But we're slowly realizing the need to have real OOP in the mainstream now, which is about message passing.
- slver 5y agoI'm not sure what the distinction is. Mainstream languages like Java have taken a subset of OOP because that's the subset they needed when they were being defined initially. But we're slowly evolving towards a concurrent async message passing models, and they're slowly being integrated into mainstream OOP languages. In effect what we call by various names like SOA, microservices, EDA, actors, and so on, is basically OOP at scale.
- deleted 5y ago[deleted]
- kwhitefoot 5y agoAbsolutely. It really confused me when I first encountered objects in Turbo Pascal 5.5 in 1989. Until then I had been programming soft real time systems where messages really were messages. The sender put the message in the queue and the recipient did something with it and possibly sent a message back. It took me days of banging my head against the wall to understand that in the object world messages were actually synchronous message calls and that the sender couldn't do anything until the object method returned.
- klibertp 5y ago> you can't design real-world systems that way in OOP languages In most OOP languages. Out of many different languages which implement various interpretations of how OO should work, I found Io to be the purest, and for some odd reasons the most practical, implementation of object orientation. In Io, you get two primitives: objects with slots and messages (which are also objects, of course). There's literally nothing else in the language. There's a bit of syntactic sugar, or rather, there can be a bit of syntactic sugar if you want, because the parser is an object you can customize on runtime, but otherwise it's just objects and messages. Message contents can be passed by name (lazily), and you can transform messages in any way you want before they get send. Or after. Taken together, you get a run-time equivalent of Lisp macros (Io is also homoiconic, btw). Add to it the fact you can reify interpreter state as first-class objects, and you get incredibly expressive language based on extremely small set of concepts. I think allowing messages to be first-class values able to exist without specyfing a target is the key here. It's also what Erlang does, with the difference being the sending and receiving messages in Erlang is hardcoded in BEAM, while it's 100% customizable in Io. My favourite example of the power of Io: someObj someMsg(some, args) this is a simple message send. It will try to look up `someMsg` slot in the target `someObj`, will traverse the prototype chain if needed, and will activate the content of the slot if it's a block of code. someObj @(someMsg(some, args)) # outer parens optional this also a simple message send, where the message name is `@`. `@` is defined in `Object`, root of all prototype chains, and it: suspends evaluation of `someMsg`, saves a pair of `someObject`+`someMsg` in a queue in a Scheduler object and returns immediately. In other words, this time the message is delivered asynchronously, and executed the next time some Coroutine (including main) yields. It's not a built-in primitive, you can implement `@` yourself pretty easily. ...anyway, when I hear about message passing, and think about it being "done right" it's "Io and Erlang" that come to mind. Very different implementations, but they both work exceptionally well.
- tinus_hn 5y agoStructs are passive data structures. Objects include these procedures with the data.