4 ms·
Messages were clearly not fancy subroutine calls in Smalltalk-72 and -74: they were a stream of tokens between the sender and the receiver. This was optimized a
by jecel 10y ago
Messages were clearly not fancy subroutine calls in Smalltalk-72 and -74: they were a stream of tokens between the sender and the receiver. This was optimized away in Smalltalk-76 (and so -78 and -80) so that messages no longer seemed like the ones in Actor languages or Erlang.
But I don't think it is unfortunate that the name "messages" has persisted. Check out Squeak running on a 56 core Tilera chip with the RoarVM. Messages from an object to another one in the same core are indeed just fancy subroutine calls. But if the receiver is in a different core, then a message is a bunch of bytes sent from one core to the other. Even though the two messages are the same at the source level and at the bytecode level.
- Animats 10y agoWhen you write i := (j + 1) this is said to be sending a "+" message to j with argument 1. But how does the value get sent back to the assignment. For consistency, it ought to be j sending an assignment message to i with the the value. But how did j find out about i? The assignment message is sent to i, not j. Problem. So the Smalltalk convention is that each message sent produces a value. That value is determined by the recipient of the message; it's not just a send status code as in a real message passing system. Values break the message passing paradigm, and force the "message passing" to work like a function call. So there was no real reason not to implement them as function calls. Then there was no real reason not to think of them as function calls. A purer message passing approach would involve a callback when the result is available. That's how everything with a delay works in Javascript. It's a bit unwieldy to do that for every piece of computation, though. Real asynchronous messages are something else. Go uses them extensively. Then you have locking, race conditions, lockups, and all the problems of concurrency, but you can get multiple processors working on the problem.
- jecel 10y agoIn Smalltalk-72 and -74 the assignment (left arrow character) was a message just like any other. This became a special case in Smalltalk-76 and later and became just a message again in Self, where you wrote i: (j + 1) meaning self i: ( self j + 1 ) For tinySelf 1 I did implement each message using future objects. This is, as you say, slooooow to do for every piece of computation but there are implementation tricks that can optimize away all the cases where it is not really needed. http://www.merlintec.com/lsi/tiny.html http://www.merlintec.com/lsi/tiny.html
- groovy2shoes 10y ago> So there was no real reason not to implement them as function calls. Then there was no real reason not to think of them as function calls. Indeed, and this was the insight discovered by Guy L. Steele Jr. while experimenting with implementing the Actor Model in Lisp, which was one of the insights that lead to the development of Scheme. At some point, while reading his code, he realized that actors and closures were implemented with roughly the same code, modulo alpha conversion, and thus a message pass was the same as a function call. Of course, the real message-passing called for by the Actor Model became more practical with the introduction of tail-call elimination, because that meant that the stack did not grow out of control as the actors delivered their results via messages to the next actor. I have a feeling that the discovery of closures-as-actors and the discovery of tail-call elimination went hand-in-hand, and possibly even continuation-passing style.