[erlang-questions] Erlang and Akka, The Sequel
ok@REDACTED
ok@REDACTED
Thu Apr 2 01:56:24 CEST 2015
> Maybe I'm splitting hairs here, but my point is that an RPC isn't a Future
> (even though the Erlang documentation on rpc:async_call/4 states that it
> implements "call streams with promises").
I don't think anyone is claiming that an RPC *is* a future.
The claim is that a Future is just a process with a peculiarly
restricted communication pattern. I mean by this that a future
in *any* programming language is just a process (abstractly
conceived) with a peculiarly restricted communication pattern.
More precisely, as originally defined in
https://www.dreamsongs.com/Files/hicss.pdf,
a future is a shared synchronisation data structure through
which that restricted communication is performed.
That was why I pointed out that in my Smalltalk library there
is no Future class; they are just joinable threads. The data
structure that represents any kind of thread can perfectly
well represent and support communication with a joinable thread.
In Java you'd use a java.util.concurrent.FutureTask or one of
its many relatives (java.util.concurrent is over the complexity
event horizon and accelerating).
I find the Java example compelling: if I want some threads
communicating in a particular way in Java, as of 1.8 it is
easier to go back to basics than to read all the library
documentation and figure out what's going on.
What saves the OTP behaviours from the same fate?
Mainly the fact that there aren't very many of them,
and the number isn't growing. Erlang/OTP seems to strike
a very nice balance between a core of simple Erlang
primitives and a limited palette of "big" behaviours that
take a lot of the work off your hands. Futures fall into
the "much needed gap".
When you might reach for a FutureTask<T> in Java, in
Erlang you would think about what the processes are
doing and how they communicate and you would look at the
constellation of communicating processes and you might
very well end up thinking that the Future-style
communication pattern was *not* after all the best.
For example, the QLISP paper says "we generalize the
notion of a future to allow several processes to be
associated with it, along with a combining function.
As each process
nishes, it calls the combining function
with the value of the form it has just
nished computing.
When all of the processes have completed, the future will
be realized." They also allow an end-test predicate so
that you can do OR-parallel searches, killing off any
remaining processes as soon as the final result is known.
This is an example of taking a step back and realising
that the restricted communication of a Future<T> is *TOO*
restricted.
> Futures can be implemented in
> Erlang using RPCs, but, to me, they're not equivalent semantic concepts.
No. RPCs are strictly more powerful than futures.
> Garrett Smith gave a great presentation at last week's Erlang conference
> about "The Timeless Way of Building Erlang Apps" (see [3] and
> erlangpatterns.org). What I think could be useful is a "pattern"
> description for futures that incorporates the implementation using RPCs. I
> may just do this if someone doesn't beat me to it.
For me, the single most important piece of advice is
DON'T THINK IN TERMS OF FUTURES AND THEN ENCODE THAT IN ERLANG.
Think in terms of processes and the communication between them.
Consider alternative communication architectures.
> It's possible that I misunderstood [2] above, but I don't think so. So to
> press on this a little more, is it possible to compose futures in Erlang
> in
> a manner similar to *Try* as shown above?
Be more specific.
Give a real example.
Are you talking about making a Future<Future<T>> instance?
Why do you want to do this?
If you started by thinking about concurrent activities
(without reference to any programming language)
and the communication patterns between them (ditto),
for what kind of problem would this be a live issue?
> To wrap up, Try, Future, Observable are useful concepts,
in some languages. ("Try" is a really *horrible* name for
ML's "option"/Haskell's "Maybe". It just doesn't work
grammatically.) "Try" is somewhere between "trivial" and
"obfuscatory". "Future" is just a strangely restricted
process, putting it also somewhere between "trivial" and
"obfuscatory" in Erlang.
> *Observable" is something I didn't cover very well except to say that it's
> is future applied to a stream. This is a bit simplistic. An *Observable*
> is something that may produce events.
In other words, it's a process that may send messages.
Sounds like any other Erlang process, really.
> To receive events, a subscription
> must be created by an *Observer*.
This whole Observer/Observable thing goes back to
the Smalltalk-80 "dependency" machinery, which is
actually a trivial chunk of Smalltalk code. It's
78 lines in my Smalltalk library, about 1/3 of that
is convenience interface and another 1/3 is locking.
The Gang Of Four book succeeded in making it look
non-trivial, indeed complex, but trivial is what it
is in any good OO language. Indeed, I'd have to
say somewhere between trivial and obfuscatory, as
it can make it quite hard to see the connections
between things.
In Erlang, one approach would be to make a simple
process that receives three basic kinds of messages:
- I want to subscribe
- I want to stop subscribing
- Here is a message to forward to the subscribers
observable:spawn(Fun) would create two processes.
The first one manages the subscriptions. Its
identity would be passed to the Fun in the other
process. It would be hard to spend a whole afternoon
implementing this.
Smalltalk teaches us a lot here. Because the original
simple machinery described in the Blue Book, in Inside
Smalltalk, and elsewhere proved to be a bit *too* simple.
Object Arts decided that you needed to be able to *test*
this stuff in SUnit, which required planting hooks for
SUnit to use.
Another complete observer implementation called
TriggerEvent was implemented. This is sometimes called
the Self-Addressed Stamped Envelope pattern.
And then Vassili Bykov implemented a *third* scheme,
called Announcements, originally for VisualWorks, but
used by a couple of other Smalltalks as well.
So the Smalltalk world has (at least) *three* observer
interfaces. Having implemented all three in my system,
it's painfully obvious that they *all* have problems.
The saving thing about Smalltalk is that none of them
needs compiler support. On the evidence, I don't trust
*anyone* to design an Observable kit well, myself included.
So you should think of "future" as a *Design Pattern*,
not a built-in type, which a programmer should be *able
to implement appropriately for their needs*, not a
ready-made strait-jacket. And you should think of
"Observable" in the same way as a *Design Pattern*,
which people can *easily implement*.
Of course, in Erlang, the existing tracing machinery
is probably a good place to start for Observable.
But you should *always* start by thinking about what
the processes want to be and how they want to communicate
*before* trying to squeeze them into no-thinking-required
moulds.
package was created and adopted by some but not all
Smalltalks.
More information about the erlang-questions
mailing list