The Scala Actors Migration Guide

Introduction

Starting with Scala 2.11.0, the Scala
Actors
library is deprecated. Already in Scala 2.10.0 the default actor library is
Akka.

To ease the migration from Scala Actors to Akka we are providing the
Actor Migration Kit (AMK). The AMK consists of an extension to Scala
Actors which is enabled by including the scala-actors-migration.jar
on a project’s classpath. In addition, Akka 2.1 includes features,
such as the ActorDSL singleton, which enable a simpler conversion of
code using Scala Actors to Akka. The purpose of this document is to
guide users through the migration process and explain how to use the
AMK.

This guide has the following structure. In Section “Limitations of the
Migration Kit” we outline the main limitations of the migration
kit. In Section “Migration Overview” we describe the migration process
and talk about changes in the Scala
distribution that make the
migration possible. Finally, in Section “Step by Step Guide for
Migrating to Akka” we show individual steps, with working examples,
that are recommended when migrating from Scala Actors to Akka’s
actors.

A disclaimer: concurrent code is notorious for bugs that are hard to
debug and fix. Due to differences between the two actor
implementations it is possible that errors appear. It is recommended
to thoroughly test the code after each step of the migration process.

Limitations of the Migration Kit

Due to differences in Akka and Scala actor models the complete functionality can not be migrated smoothly. The following list explains parts of the behavior that are hard to migrate:

Relying on termination reason and bidirectional behavior with link method - Scala and Akka actors have different fault-handling and actor monitoring models.
In Scala linked actors terminate if one of the linked parties terminates abnormally. If termination is tracked explicitly (by self.trapExit) the actor receives
the termination reason from the failed actor. This functionality can not be migrated to Akka with the AMK. The AMK allows migration only for the
Akka monitoring
mechanism. Monitoring is different than linking because it is unidirectional and the termination reason is now known. If monitoring support is not enough, the migration
of link must be postponed until the last possible moment (Step 5 of migration).
Then, when moving to Akka, users must create an supervision hierarchy that will handle faults.

Usage of the restart method - Akka does not provide explicit restart of actors so we can not provide the smooth migration for this use-case.
The user must change the system so there are no usages of the restart method.

Usage of method getState - Akka actors do not have explicit state so this functionality can not be migrated. The user code must not
have getState invocations.

Not starting actors right after instantiation - Akka actors are automatically started when instantiated. Users will have to
reshape their system so it starts all the actors right after their instantiation.

Method mailboxSize does not exist in Akka and therefore can not be migrated. This method is seldom used and can easily be removed.

Migration Overview

Migration Kit

In Scala 2.10.0 actors reside inside the Scala distribution as a separate jar ( scala-actors.jar ), and
the their interface is deprecated. The distribution also includes Akka actors in the akka-actor.jar.
The AMK resides both in the Scala actors and in the akka-actor.jar. Future major releases of Scala will not contain Scala actors and the AMK.

To start the migration user needs to add the scala-actors.jar and the scala-actors-migration.jar to the build of their projects.
Addition of scala-actors.jar and scala-actors-migration.jar enables the usage of the AMK described below.
These artifacts reside in the Scala Tools
repository and in the Scala distribution.

Step by Step Migration

Actor Migration Kit should be used in 5 steps. Each step is designed to introduce minimal changes
to the code base and allows users to run all system tests after it. In the first four steps of the migration
the code will use the Scala actors implementation. However, the methods and class signatures will be transformed to closely resemble Akka.
The migration kit on the Scala side introduces a new actor type (ActWithStash) and enforces access to actors through the ActorRef interface.

It also enforces creation of actors through special methods on the ActorDSL object. In these steps it will be possible to migrate one
actor at a time. This reduces the possibility of complex errors that are caused by several bugs introduced at the same time.

After the migration on the Scala side is complete the user should change import statements and change
the library used to Akka. On the Akka side, the ActorDSL and the ActWithStash allow
modeling the react construct of Scala Actors and their life cycle. This step migrates all actors to the Akka back-end and could introduce bugs in the system. Once code is migrated to Akka, users will be able to use all the features of Akka.

Step by Step Guide for Migrating to Akka

In this chapter we will go through 5 steps of the actor migration. After each step the code can be tested for possible errors. In the first 4
steps one can migrate one actor at a time and test the functionality. However, the last step migrates all actors to Akka and it can be tested
only as a whole. After this step the system should have the same functionality as before, however it will use the Akka actor library.

Step 1 - Everything as an Actor

The Scala actors library provides public access to multiple types of actors. They are organized in the class hierarchy and each subclass
provides slightly richer functionality. To make further steps of the migration easier we will first change each actor in the system to be of type Actor.
This migration step is straightforward since the Actor class is located at the bottom of the hierarchy and provides the broadest functionality.

The Actors from the Scala library should be migrated according to the following rules:

class MyServ extends Reactor[T] -> class MyServ extends Actor

Note that Reactor provides an additional type parameter which represents the type of the messages received. If user code uses
that information then one needs to: i) apply pattern matching with explicit type, or ii) do the downcast of a message from
Any to the type T.

class MyServ extends ReplyReactor -> class MyServ extends Actor

class MyServ extends DaemonActor -> class MyServ extends Actor

To pair the functionality of the DaemonActor add the following line to the class definition.

override def scheduler: IScheduler = DaemonScheduler

Step 2 - Instantiations

In Akka, actors can be accessed only through the narrow interface called ActorRef. Instances of ActorRef can be acquired either
by invoking an actor method on the ActorDSL object or through the actorOf method on an instance of an ActorRefFactory.
In the Scala side of AMK we provide a subset of the Akka ActorRef and the ActorDSL which is the actual singleton object in the Akka library.

This step of the migration makes all accesses to actors through ActorRefs. First, we show how to migrate common patterns for instantiating
Scala Actors. Then we show how to overcome issues with the different interfaces of ActorRef and Actor, respectively.

Actor Instantiation

The translation rules for actor instantiation (the following rules require importing scala.actors.migration._):

All accesses to the object MyActor should be replaced with accesses to MyActor.ref.

Note that Akka actors are always started on instantiation. In case actors in the migrated
system are created and started at different locations, and changing this can affect the behavior of the system,
users need to change the code so actors are started right after instantiation.

Remote actors also need to be fetched as ActorRefs. To get an ActorRef of an remote actor use the method selectActorRef.

Different Method Signatures

At this point we have changed all the actor instantiations to return ActorRefs, however, we are not done yet.
There are differences in the interface of ActorRefs and Actors so we need to change the methods invoked on each migrated instance.
Unfortunately, some of the methods that Scala Actors provide can not be migrated. For the following methods users need to find a workaround:

getState() - actors in Akka are managed by their supervising actors and are restarted by default.
In that scenario state of an actor is not relevant.

restart() - explicitly restarts a Scala actor. There is no corresponding functionality in Akka.

All other Actor methods need to be translated to two methods that exist on the ActorRef. The translation is achieved by the rules described below.
Note that all the rules require the following imports:

Additionally rules 1-3 require an implicit Timeout with infinite duration defined in the scope. However, since Akka does not allow for infinite timeouts, we will use
100 years. For example:

implicit val timeout = Timeout(36500 days)

Rules:

!!(msg: Any): Future[Any] gets replaced with ?. This rule will change a return type to the scala.concurrent.Future which might not type check.
Since scala.concurrent.Future has broader functionality than the previously returned one, this type error can be easily fixed with local changes:

actor !! message -> respActor ? message

!![A] (msg: Any, handler: PartialFunction[Any, A]): Future[A] gets replaced with ?. The handler can be extracted as a separate
function and then applied to the generated future result. The result of a handle should yield another future like
in the following example:

Public methods that are not mentioned here are declared public for purposes of the actors DSL. They can be used only
inside the actor definition so their migration is not relevant in this step.

Step 3 - Actors become ActWithStashs

At this point all actors inherit the Actor trait, we instantiate actors through special factory methods,
and all actors are accessed through the ActorRef interface.
Now we need to change all actors to the ActWithStash class from the AMK. This class behaves exactly the same like Scala Actor
but, additionally, provides methods that correspond to methods in Akka’s Actor trait. This allows easy, step by step, migration to the Akka behavior.

To achieve this all classes that extend Actor should extend the ActWithStash. Apply the
following rule:

class MyActor extends Actor -> class MyActor extends ActWithStash

After this change code might not compile. The receive method exists in ActWithStash and can not be used in the body of the act as is. To redirect the compiler to the previous method
add the type parameter to all receive calls in your system. For example:

Additionally, to make the code compile, users must add the override keyword before the act method, and to create
the empty receive method in the code. Method act needs to be overridden since its implementation in ActWithStash
mimics the message processing loop of Akka. The changes are shown in the following example:

ActWithStash instances have variable trapExit set to true by default. If that is not desired set it to false in the initializer of the class.

The remote actors will not work with ActWithStash out of the box. The method register('name, this) needs to be replaced with:

registerActorRef('name, self)

In later steps of the migration, calls to registerActorRef and alive should be treated like any other calls.

After this point user can run the test suite and the whole system should behave as before. The ActWithStash and Actor use the same infrastructure so the system
should behave exactly the same.

Step 4 - Removing the act Method

In this section we describe how to remove the act method from ActWithStashs and how to
change the methods used in the ActWithStash to resemble Akka. Since this step can be complex, it is recommended
to do changes one actor at a time. In Scala, an actor’s behavior is defined by implementing the act method. Logically, an actor is a concurrent process
which executes the body of its act method, and then terminates. In Akka, the behavior is defined by using a global message
handler which processes the messages in the actor’s mailbox one by one. The message handler is a partial function, returned by the receive method,
which gets applied to each message.

Since the behavior of Akka methods in the ActWithStash depends on the removal of the act method we have to do that first. Then we will give the translation
rules for translating individual methods of the scala.actors.Actor trait.

Removal of act

In the following list we present the translation rules for common message processing patterns. This list is not
exhaustive and it covers only some common patterns. However, users can migrate more complex act methods to Akka by looking
at existing translation rules and extending them for more complex situations.

A note about nested react/reactWithin calls: the message handling
partial function needs to be expanded with additional constructs that
bring it closer to the Akka model. Although these changes can be
complicated, migration is possible for an arbitrary level of
nesting. See below for examples.

A note about using receive/receiveWithin with complex control
flow: migration can be complicated since it requires refactoring the
act method. A receive call can be modeled using react and
andThen on the message processing partial function. Again, simple
examples are shown below.

If there is any code in the act method that is being executed before the first loop with react that code
should be moved to the preStart method.

PFCatch is not included in the AMK as it can stay as the permanent feature in the migrated code
and the AMK will be removed with the next major release. Once the whole migration is complete fault-handling
can also be converted to the Akka supervision.

Changing Actor Methods

After we have removed the act method we should rename the methods that do not exist in Akka but have similar functionality. In the following list we present
the list of differences and their translation:

exit()/exit(reason) - should be replaced with context.stop(self)

receiver - should be replaced with self

reply(msg) - should be replaced with sender ! msg

link(actor) - In Akka, linking of actors is done partially by supervision
and partially by actor monitoring. In the AMK we support
only the monitoring method so the complete Scala functionality can not be migrated.

The difference between linking and watching is that watching actors always receive the termination notification.
However, instead of matching on the Scala Exit message that contains the reason of termination the Akka watching
returns the Terminated(a: ActorRef) message that contains only the ActorRef. The functionality of getting the reason
for termination is not supported by the migration. It can be done in Akka, after the Step 4, by organizing the actors in a supervision hierarchy.

If the actor that is watching does not match the Terminated message, and this message arrives, it will be terminated with the DeathPactException.
Note that this will happen even when the watched actor terminated normally. In Scala linked actors terminate, with the same termination reason, only if
one of the actors terminates abnormally.

If the system can not be migrated solely with watch the user should leave invocations to link and exit(reason) as is. However since act() overrides the Exit message the following transformation
needs to be applied:

NOTE: There is another subtle difference between Scala and Akka actors. In Scala, link/watch to the already dead actor will not have affect.
In Akka, watching the already dead actor will result in sending the Terminated message. This can give unexpected behavior in the Step 5 of the migration guide.

Step 5 - Moving to the Akka Back-end

At this point user code is ready to operate on Akka actors. Now we can switch the actors library from Scala to
Akka actors. To do this configure the build to exclude the scala-actors.jar and the scala-actors-migration.jar,
and to include akka-actor.jar and typesafe-config.jar. The AMK is built to work only with Akka actors version 2.1 which are included in the Scala distribution
and can be configured by these instructions.

After this change the compilation will fail due to different package names and slight differences in the API. We will have to change each imported actor
from scala to Akka. Following is the non-exhaustive list of package names that need to be changed:

Also, method declarations def receive = in ActWithStash should be prepended with override.

In Scala actors the stash method needs a message as a parameter. For example:

def receive = {
...
case x => stash(x)
}

In Akka only the currently processed message can be stashed. Therefore replace the above example with:

def receive = {
...
case x => stash()
}

Adding Actor Systems

The Akka actors are organized in Actor systems.
Each actor that is instantiated must belong to one ActorSystem. To achieve this add an ActorSystem instance to each actor instantiation call as a first argument. The following example shows the transformation.

To achieve this transformation you need to have an actor system instantiated. The actor system is usually instantiated in Scala objects or configuration classes that are global to your system. For example:

val system = ActorSystem("migration-system")

Then apply the following transformation:

ActorDSL.actor(...) -> ActorDSL.actor(system)(...)

If many calls to actor use the same ActorSystem it can be passed as an implicit parameter. For example:

Finally, Scala programs are terminating when all the non-daemon threads and actors finish. With Akka the program ends when all the non-daemon threads finish and all actor systems are shut down.
Actor systems need to be explicitly terminated before the program can exit. This is achieved by invoking the shutdown method on an Actor system.

Remote Actors

Once the code base is moved to Akka remoting will not work any more. The methods registerActorFor and alive need to be removed. In Akka, remoting is done solely by configuration and
for further details refer to the Akka remoting documentation.

Examples and Issues

All of the code snippets presented in this document can be found in the Actors Migration test suite as test files with the prefix actmig.