Getting Started - laforge49/Asynchronous-Functional-Programming GitHub Wiki

##What is an Actor?

An actor is a kind of object. The main difference between objects and actors is that you call functions on an object, but you pass messages to an actor. The advantage to doing this is that you separate control flow from data flow. For when you call a function on an object, the object "does its thing" and then returns some result. But when you pass a message to an actor, well, that's it. At some time, sooner or later, the actor will process the message and send back the results. OK, we have a challenge here. How do you use actors and keep the code understandable?

Fortunately, in Scala it is relatively painless to write an anonymous callback.

someActor(somethingToDo) { result => println(result) }

We can even chain things together.

actor1(request) { result1 =>
  actor2(result1) { result2 => println(result2) }
}

##Processing a Message in an Actor

For every type of message that an actor receives, it has a function. And all the functions which an actor uses to process these messages have the same signature. So lets begin by defining some message classes and some functions to process them.

case class Times2(value: Int)
case class Add(value1: Int, value2: Int)

private def times2(msg: AnyRef, rf: Any => Unit) { rf(msg.asInstanceOf[Times2].value * 2) }
private def add(msg: AnyRef, rf: Any => Unit) {
  val req = msg.asInstanceOf[Add]
  rf(req.value1 + req.value2)
}

Functions which process the messages sent to an actor have two parameters. The first is the message; the second is a callback function used to pass back the results.

##Binding Functions to Message Classes

An actor needs to invoke the appropriate function for each message it receives. This is done using a table which is initialized with the bind function.

bind(classOf[Times2], times2)
bind(classOf[Add], add)

##A Simple Actor

Here then is the completed code.

class SimpleActor extends Actor {
  bind(classOf[Times2], times2)
  bind(classOf[Add], add)
  private def times2(msg: AnyRef, rf: Any => Unit) { rf(msg.asInstanceOf[Times2].value * 2) }
  private def add(msg: AnyRef, rf: Any => Unit) {
    val req = msg.asInstanceOf[Add]
    rf(req.value1 + req.value2)
  }
}

##Testing an Actor

Actors are really designed only to work with each other, but sometimes we do need to do some testing. For this we use the Future class.

The problem with actors is that you don't (or at least your code shouldn't) know when an actor will produce a result. It may be operating on a different thread, or it may process the message synchronously. Actors, of course, are designed to handle this. The Future class can also deal with this, though not as well. But lets look at some test code first before we discuss the issue further.

val simpleActor = new SimpleActor
println(Future(simpleActor, Times2(21)))
println(Future(simpleActor, Add(1, 2)))

This code prints 42 and then prints 3. Notice that Future does not take a callback. You just give it an actor and a message and Future returns the result. Easy enough to use, but it comes with a hidden cost, and a high one.

The thing is, Future sends a message to the actor and then waits. So the thread that is running Future can't do anything else until the actor passes its results back. Turns out, a thread has a huge memory footprint. So while the Future class is great for testing, your production code isn't going to scale very well if it uses this class.

Code

Tutorial