[erlang-questions] Erlang and Akka, The Sequel
Youngkin, Rich
richard.youngkin@REDACTED
Mon Mar 30 03:56:08 CEST 2015
Hi all,
First of all, my apologies for the long email. The subject is somewhat
esoteric and therefore not easily expressed in a short email.
I'm following up on this email thread [1] from a slightly different
perspective. This time I'd like to focus on the abstractions supported by
Erlang vs. Akka rather than questions regarding availability of skilled
developers or the technical superiority of one VM vs. the other.
Akka, on the surface, has first-class concepts that aren't supported by
Erlang (e.g., Try, Future, Observable). By "first-class" I mean
concepts/capabilities that are directly supported in the language and/or
framework. For example, Erlang/OTP includes capabilities and concepts such
as gen_server. Libraries (e.g., poolboy) address those that aren't
available in Erlang/OTP. [2] almost addresses some of those questions,
specifically Fred Hebert's and Garrett Smith's responses.
Fred's response in [2] is interesting and informative, but it misses the
mark in that it's up to the developer to know/understand how and when to
implement the appropriate pattern. That's what capabilities such as
gen_server are intended to eliminate. It gets even more interesting when
discussing composable operations a la Akka. Here are 2 concepts in Akka
that as far as I know aren't directly supported in Erlang. These only
illustrate what's possible with the "Try" concept, but are also applicable
to Future and Observable.
1. The concept that an operation might fail (i.e., the "Try" type in Akka).
2. Being able to easily compose operations that might fail in a way where
the logic to detect and react to the failure can be expressed separately
from the main logic path, aka the "happy path".
Here's an example of the first (in Scala, taken from the Coursera course on
Principles of Reactive Programming). It's simplistic, but I think it's
applicable in more complicated use cases. Hopefully the Scala is pretty
self-explanatory:
1 val coins: Try[List[Coin]] = adventure.collectCoins()
2
3 val treasure: Try[Treasure] = coins match {
4 case Success(cs) => adventure.buyTreasure(cs)
5 case failure @ Failure(t) => failure
6 }
Basically what this is explicitly saying is that adventure.collectCoins()
may fail and the success or failure of that operation can be used in the
next operation which is to buy treasure using those coins. At this point
this is just expressing the semantics of the operation directly in the
code, namely that the operation may fail (as is also the case in
buyTreasure). This isn't a programming error, it's just expressing that
perhaps the adventure character couldn't collect coins and/or wasn't able
to buy treasure (maybe they didn't have enough coins). So Erlang's "Let It
Crash" philosophy isn't applicable. It can be argued that the block at
lines 3-5 just a case expression, but this misses the point that the
language directly expresses, through Try[], that both collectCoins() and
buyTreasure can fail.
The example above is interesting from a semantic perspective, but it's
mixing the happy path with failure handling. This is where the next
concept/capability of Akka is interesting, namely the ability to compose
operations while separating the failure handling path from the "happy
path". Here's the follow-on example from the same course:
1 val treasure: Try[Treasure] =
2 adventure.collectCoins().flatMap(coins => {
3 adventure.buyTreasure(coins)
4 })
5
6 treasure match {
7 case Success(successValue) =>
9 do something like continue to the next challenge...
10 case Failure(errorValue) =>
11 do something like make the character repeat the previous challenge...
So the "happy path" of collectCoins() and buyTreasure() isn't intermingled
with what to do if one or both of these operations fail. Specifically,
buyTreasure() won't throw an exception if collectCoins() fails. I don't
know of any way to express this in Erlang.
As far as I know Erlang doesn't support "Try" and the associated use of it
in "match". In Akka this support is provided by monads. erlando is an
Erlang library intended to provide monad support, but monads aren't baked
into the language. And "Try" just applies to synchronous operations.
"Future" implements the same semantics to async operations. "Observable"
implements the same semantics to asynchronous stream operations.
I don't have enough real-world experience in whether or not these concepts
are encountered in day-to-day "reactive" programming in Erlang or
Scala/Akka, but they do seem useful. This leads me to my questions:
1. Are these concepts generally useful or just interesting from an academic
perspective?
2. Would it be useful to support these capabilities as first-class concepts
in Erlang (similar to gen_servers)? Or is this so trivial in Erlang that
it's not worth making these first class capabilities?
3. Is there any way to express these capabilities in Erlang (in addition to
the rpc:async_call as described by Fred in [2], which only covers Futures,
and doesn't support composition)? If there is I think a description of the
pattern would be generally useful (Here's a plug for Garrett's new
erlangpatterns.org).
4. Is there anything else significant to discuss about this?
Thanks,
Rich
[1] http://erlang.org/pipermail/erlang-questions/2014-August/080699.html
[2] http://erlang.org/pipermail/erlang-questions/2012-November/070679.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20150329/9c2aca40/attachment.htm>
More information about the erlang-questions
mailing list