How are the multiple Actors implementation in Scala different?

With the release of Scala 2.9.0, the Typesafe Stack was also announced, which combines the Scala language with the Akka framework. Now, though Scala has actors in its standard library, Akka uses its own implementation. And, if we look for other implementations, we'll also find that Lift and Scalaz have implementations too!

So, what is the difference between these implementations?


This answer isn't really mine. It was produced by Viktor Klang (of Akka fame) with the help of David Pollak (of Lift fame), Jason Zaugg (of Scalaz fame), Philipp Haller (of Scala Actors fame).

All I'm doing here is formatting it (which would be easier if Stack Overflow supported tables).

There are a few places I'll fill later when I have more time.

Design Philosophy

  • Scalaz Actors

    Minimal complexity. Maximal generality, modularity and extensibility.

  • Lift Actors

    Minimal complexity, Garbage Collection by JVM rather than worrying about an explicit lifecycle, error handling behavior consistent with other Scala & Java programs, lightweight/small memory footprint, mailbox, statically similar to Scala Actors and Erlang actors, high performance.

  • Scala Actors

    Provide the full Erlang actor model in Scala, lightweight/small memory footprint.

  • Akka Actors

    Simple and transparently distributable, high performance, lightweight and highly adaptable.

Versioning

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Current stable ver. 5               2.1             2.9.0           0.10
Minimum Scala ver.  2.8             2.7.7                           2.8
Minimum Java ver.                   1.5             1.5             1.6

Actor Model Support

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Spawn new actors    Yes             Yes             Yes             Yes
inside of actor
Send messages to    Yes             Yes             Yes             Yes
known actor 
Change behavior     Actors are      Yes             Yes: nested     Yes:
for next message    immutable                       react/receive   become/unbecome
Supervision         Not provided    No              Actor: Yes,     Yes
(link/trapExit)                                     Reactor: No

Level of state isolation

If user defines public methods on their Actors, are they callable from the outside?

  • Scalaz Actors: n/a. Actor is a sealed trait.
  • Lift Actors: Yes
  • Scala Actors: Yes
  • Akka Actors: No, actor instance is shielded behind an ActorRef.

Actor type

  • Scalaz Actors: Actor[A] extends A => ()
  • Lift Actors: LiftActor, SpecializeLiftActor[T]
  • Scala Actors: Reactor[T], Actor extends Reactor[Any]
  • Akka Actors: Actor[Any]

Actor lifecycle management

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Manual start        No              No              Yes             Yes
Manual stop         No              No              No              Yes
Restart-on-failure  n/a             Yes             Yes             Configurable per actor instance
Restart semantics                   n/a             Rerun actor     Restore actor to stable state by re-allocating it and
                                                    behavior        throw away the old instance
Restart configurability             n/a             n/a             X times, X times within Y time
Lifecycle hooks provided            No lifecycle    act             preStart, postStop, preRestart, postRestart

Message send modes

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Fire-forget         a ! message     actor ! msg     actor ! msg     actorRef ! msg
                    a(message)
Send-receive-reply  (see 1)         actor !? msg    actor !? msg    actorRef !! msg
                                    actor !! msg
Send-receive-future (see 2)                         actor !! msg    actorRef !!! msg
Send-result-of-     promise(message).                               future.onComplete( f => to ! f.result )
future              to(actor)
Compose actor with  actor comap f   No              No              No
function            (see 3)

(1) Any function f becomes such an actor:

val a: Msg => Promise[Rep] = f.promise
val reply: Rep = a(msg).get

(2) Any function f becomes such an actor:

val a = f.promise
val replyFuture = a(message)

(3) Contravariant functor: actor comap f. Also Kleisli composition in Promise.

Message reply modes

TBD

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
reply-to-sender-in-message
reply-to-message

Message processing

Supports nested receives?

  • Scalaz Actors: --
  • Lift Actors: Yes (with a little hand coding).
  • Scala Actors: Yes, both thread-based receive and event-based react.
  • Akka Actors: No, nesting receives can lead to memory leaks and degraded performance over time.

Message Execution Mechanism

TBD

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Name for Execution Mechanism
Execution Mechanism is
configurable
Execution Mechanism can be
specified on a per-actor basis
Lifecycle of Execution Mechanism
must be explicitly managed
Thread-per-actor execution
mechanism
Event-driven execution mechanism
Mailbox type
Supports transient mailboxes
Supports persistent mailboxes

Distribution/Remote Actors

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Transparent remote  n/a             No              Yes             Yes
actors
Transport protocol  n/a             n/a             Java            Akka Remote Protocol
                                                    serialization   (Protobuf on top of TCP)
                                                    on top of TCP
Dynamic clustering  n/a             n/a             n/a             In commercial offering

Howtos

TBD

                    Scalaz Actors   Lift Actors     Scala Actors    Akka Actors
Define an actor
Create an actor instance
Start an actor instance
Stop an actor instance

  • scala.actors was the first serious attempt to implement Erlang-style concurrency in Scala that has inspired other library designers for making a better (in some cases) and more performant implementations. The biggest problem (at least for me), is that unlike Erlang processes, complemented with OTP (that allows for building fault-tolerant systems), scala.actors only offer a good foundation, a set of stable primitives that must be used for building a more high-level frameworks - at the end of the day, you’ll have to write your own supervisors, catalogs of actors, finite state machines, etc. on top of actors.

  • And here Akka comes to the rescue, offering a full-featured stack for actor-based development: more idiomatic actors, set of high-level abstractions for coordination (load balancers, actor pools, etc.) and building fault-tolerant systems (supervisors, ported from OTP, etc.), easily configurable schedulers (dispatchers), and so on. Sorry, if I sound rude, but I think, there will be no merge in 2.9.0+ - I’d rather expect Akka actors to gradually replace stdlib implementation.

  • Scalaz. Normally I have this library in the list of dependencies of all my projects, and when, for some reason, I can’t use Akka, non-blocking Scalaz Promises (with all the goodness, like sequence) combined with the standard actors are saving the day. I never used Scalaz actors as a replacement for scala.actors or Akka, however.


Actors: Scala 2.10 vs Akka 2.3 vs Lift 2.6 vs Scalaz 7.1

Test code & results for average latency and throughput on JVM 1.8.0_x.