Thanks for sharing some of the thinking behind this. The rest of this message is In My Humble Opinion, so I hope no one reading gets pissed by it.
Your earlier response coupled with this one (“ATOM mode originally felt like a beautiful solution”) worried me. It worried me that maybe I didn’t understand the Actor model properly. So, I went internet spelunking and found a great video of Carl Hewitt explaining Actors. See it here:
I took some notes from watching this ~40m video. I reproduce them below (some with time stamps). But I want to draw attention to a few things here. These are all consistent with what I learned about actors Long Ago.
* ATOM / pipeline mode only makes sense if the Actor is purely functional (referential transparency).
* There is NO REQUIREMENT that Actors pipeline messages. They may act on them one at a time without violating the axiom.
* Actor model does not care about message ordering. Great example in video about the indeterminacy of a bank balance and how it changes depending on the order of messages it receives. For message ordering, implement an actor to enforce FIFO/sequencing.
What I get from the above data points is that celluloid made an incorrect choice in preferring ATOM mode by default. IMHO, most programmers are not creating purely functional actors. Instead, they are using actors to enforce serialized access to a mutable property. Therefore, pipelining actually works against their goal. Secondly, most programmers will want to enforce ordering on those messages so the Actor mailbox should act FIFO. (Celluloid mostly allows this except for high-priority SystemEvent messages.)
Anyway, pipelining is an implementation detail that I think works against the goals of a lot of programmers looking to simplify parallelizing their work. And unfortunately, from an engineering and design standpoint, it also greatly complicated the inner workings of celluloid.
I’ve been dreaming about celluloid classes and event flows lately. Or should I say, I’ve had nightmares about it. :)
————
Actor Axiom
An Actor is defined thusly...
When actor receives a message it can:
* create new actors
* send messages to actors for which it has addresses
* say/designate what it will do with next message it receives
** state change?
Pipelining makes sense when next message will be processed exactly
the same as the last message (each message has no side effect on
actor state). Therefore, it only makes sense in a "functional
programming" sense (referential transparency).
There is no requirement that actors pipeline. One-at-a-time processing
is acceptable.
Futures are Actors. Can return values or exceptions.
(7:20) Put future into a message to yourself, and now you don't deadlock.
One address can be used for multiple actors.
Also, one actor may have many addresses (that potentially forward to
each other).
(11:40) When actors interact over a network, you encrypt your address and send
it out. If a reply comes back and doesn't decrypt to a valid actor
address then it should be rejected.
Messages sent between actors are "best effort" and messages may be
lost. However, between machines they should ack messages. Messages are
delivered at most once (0 or 1 times), no dupes.
Can use channels with put & get, but that isn't part of the Actor
model. A channel is actually another actor. Enforcing message ordering
can be handles by a Queue/Sequencer actor.
(18:40) Actors are indeterminant.
(24:00) Actors process one message at a time but it is INDETERMINANT which
message will arrive next. For example, let's say a checking account
has $5. From different parts of the planet we send messages to
withdraw $4 and $3. If the $4 message arrives first, the balance
reduces to $1 and the $3 message raises an exception. Conversely,
if the $3 message arrived first, the balance would be $2 and the
$4 message would raise an exception.