Good question, it seems like all definitions of message passing involve not sharing state. Even OOP emphasizes data hiding as a core practice.
Good question, it seems like all definitions of message passing involve not sharing state. Even OOP emphasizes data hiding as a core practice.
When "passing a message" is a heavier operation syntactically than "calling a function" is, one thinks of the two as being more fundamentally different than they really are. I find that interesting because it's superficial yet influential.
Do Erlang programmers think of function calls as a kind of message passing? or as totally different things? How far apart are the two, psychologically? Is sync vs. async the more fundamental distinction?
Syntactic difference is big, but neither function call nor message send are heavy:
func({my, params, in, a tuple})
proc ! {my, params, in, a tuple}
The difference is that function call returns something, message send just happens and we have no guarantee at all what is going to happen with the message.We frequently write functions which send message and wait for reply for a certain amount of time, and then return this reply or error if no reply was sent.
So, in Erlang there is - I think - huge difference between functions and messages, but that is because it's functions are just that: functions.
The situation is quite different in Smalltalk, where you have no functions at all, only objects. Everything there already is a message being passed, and so the difference between asynchronous and synchronous message should be very minor. I don't know Smalltalk well enough, but I suspect there are libraries implementing async message passing in Smalltalk and that they are almost indistinguishable from normal Smalltalk code, if at all.
But this seems to confuse paulhodge's #2 and #3. Messages as first-class values don't give you asynchronous messaging for free. Or do you just mean that syntactically the async code would look like normal sync code? That's true of JS as well, of course, and it's a problem. Async vs. sync is a fundamental distinction for the programmer, and one wants help to keep track of what's going on. I suspect Erlang got it very right in having two lightweight but distinct notations for these two deeply distinct things. What other languages do that?
Edit: It occurred to me that you said something profound:
> For me, in Erlang code, function call is about 'here', while sending a message is about 'there'.
"Here" vs. "there" is a spatial distinction; "sync" vs. "async" is a temporal one. The temporal distinction is far harder to get one's (my) head around, perhaps because code itself is laid out spatially, so you constantly have to recreate its temporal model in your head when you read it, and that's taxing. It's far easier to think of code as divided up spatially. We build up a system by making repeated 'inner' vs 'outer' distinctions - i.e. modularity. That structure is static while the temporal structure is dynamic and unpredictable. To write good async code you have to get in the habit of thinking "who knows when". But you never have to think "who knows where". The code tells you where.
This makes me think that there is wisdom in Erlang's decision to identify the temporal distinction (sync vs. async) with a spatial one (inside a process vs. outside it) — thus guaranteeing a translation from the harder mental category to the easier one — and to provide distinct notations for there-and-async vs. here-and-sync, so you always know which is which. Total complexity seems greatly reduced this way compared to a model in which the two distinctions are orthogonal and can combine in arbitrary ways.
For Erlang to say that between objects (i.e. processes) things are async and pass messages, while within an object they are sync and call functions, is a major departure from the Smalltalkian "everything is the same everywhere" small-and-regular philosophy. I'm fond of small-and-regular designs, but sync vs. async is one of those distinctions that is so fundamental, it's folly not to have it in the core unless you intend to get away with pretending that everything is always synchronous.