Hacker News new | ask | show | jobs
by artemonster 700 days ago
I never liked „OOPs world“ obsession with a receiver. Why the hell receiver of a message gets to dispatch the implementation at very late moment? Why not context? And when there are many receivers? I.e. a composite object of an int and a float receive message „add“ - who decides which implementation to use and why the heck any of them may even know how to add self to another? There were many attempts to solve this and all were horrible (i.e. extension methods). I have been experimenting with these concepts too (i.e. a receiverless message is a throw, which will be picked up by an enclosing effect handler via pattern match), but its all unreadable and unmaintainble mess. Anyone doing the same thing? I would love to exchange thoughts :) extras to read: ian piumarta‘s papers on his OOP system and a paper on Korz programming language
5 comments

> Why the hell receiver of a message gets to dispatch the implementation at very late moment? Why not context?

Well, then it is the context who is the actual receiver, isn't it? Since in this scenario the object may never even receive the message since the context has already processed it on its own, so calling it a "receiver" would be incorrect.

> a composite object of an int and a float receive message „add“ - who decides which implementation to use

The composite object itself, who else? It can do anything, including doing nothing, or not using any of their implementations, or dividing its int by its float, etc.

Maybe I'm misunderstanding but the whole point of any messaging system is that the sender "doesn't know how to process the message" and it's up to the receivers to process it and coordinate amongst themselves. Otherwise you could just do the processing where the message was sent. For non-OOP examples you have most service architectures.

So from that perspective an int-float object would be built by composition and the containing object would receive and process the message before dispatching to it's component objects as it saw fit to accomplish the task of being an int-float object.

In OOP as I understood it, messages are first-class entities at the same level as "objects". This is strikingly different from modern OOP languages where a message is conflated with "function calls". This is pointed out in the article: "[I]n Smalltalk, messages 'were not real messages but disguised synchronous function calls', and this mistake was also repeated in other languages[...]".

If a message is a first class entity, then an object can technically have only one "function call" -- receive message.

So that an object can simulate an arbitrary set of functions with arbitrary arguments, the implementation of the "receive message" function cannot impose conditions on the format or content of the message. Instead, the message must be encoded in a self-describing format so that the object can interrogate the message and its contents, and only then decide what to do with that message, up to an including ignoring the message entirely.

To make this more concrete, imagine having an JavaScript object that has only one method: receive messages encoded as JSON strings. With JSON strings we can say that the message is self-describing and is easily parsed by the object. Once the JSON string is parsed, the object can then decide what to do based on the content of the message. This is both a late-binding and a dispatching activity.

It should be clear that the version of OOP does not include anything about types. That's because OOP was designed with LISP-like languages in mind, where symbols were processed and strongly-typed objects. It also means that build-/complile-time checking wasn't possible.

I'd say the modern web with JavaScript and HTTP calls is more like the original OOP design than any modern "OOP"-like programming language.

Does send() and method_missing() in Ruby fit that bill? From what I remember from Ruby, and it’s been a while, all method calls are just a message via send() with a symbol and arguments. Normal method calls are just syntactic sugar over this system. With method_missing() you can handle any messages that use a symbol that doesn’t match a method name. You could make the object handle messages in a completely dynamic way.
Yeah I was intentionally not talking about OOP but about messaging because I think the concept quite obviously transcends the programming paradigm and I often use it as you suggest.

That said one way of processing messages is to relate the message name to a method name at runtime and that is quite successful which is where I think the stronger version of that linking comes from in other languages that have static binding. It's still the same messaging concept but the routing is resolved at compile time which has tradeoffs.

then why the receiver would know too? There are more parties in this orgy: message receiver, message sender (method caller), environment (static: like imports), context (dynamic: like stack). Why the heck of all of those the receiver decides?
I'd take all of those as receivers if they are reacting to a message being sent.

To give another example in a game engine I recently worked on we sent messages between game entities and the sending entity could target itself directly and indirectly with no problems and a common pattern to keep components of the entity decoupled was to do just that.

In some OOP languages the environment and everything else are all objects. I think Smalltalk itself works that way. Which reflects this way of thinking.

If you want a "receiverless message", you want a queue surely?
possibly related food for thought: google up DCI?

https://duckduckgo.com/?q=trygve+dci

Why not generic methods like in Common Lisp?