Reproducing Erlang’s event handler pattern in Akka

I like creating systems. Systems in the cybernetics sense of the word: components and connections between them. This is why I am trying to find programming languages that enable this type of programming. My current obsession is Erlang. First, a small digression…

Erlang

In my opinion Erlang is not very easy to learn because of a few things:

  1. Syntax
  2. Paradigm

The first is not much of a problem. It can be overcome by lots of staring at the code until it becomes to make sense. In fact, if you showed modern C# or Java to a programmer 15 years ago, they wouldn’t understand it that well either.

It is the second thing on that list — paradigm, that makes Erlang particularly hard to get into. This is because the way you would implement a system in Erlang is not the same way you would implement a system in Python or C++. This is also the reason why I think learning Erlang is so valuable — you get to harvest all the patterns that have had time to stew and develop over the last 30 years!

Getting on with it

Anyways, let’s get on with it! The best way to learn is to do — so let’s take a piece of Erlang code and try to implement it with Akka. The aim of this exercise is twofold:

  1. We want to see if Akka can pull it off — or can Erlang do something magical that no one can?
  2. Learn something — by re-implementing the code in a different language we are truly forced to understand the thing (and not merely think we do)

In the following exercise we use the Programming Erlang, 2nd Edition by Joe Armstrong. The source code for Akka project can be found in my public bitbucket repository at https://bitbucket.org/drozdyuk/akka-patterns/


The Event handler pattern

Joe begins the explanation with the following code:

% event_handler.erl
-module(event_handler).
-export([make/1, add_handler/2, event/2]).
%% make a new event handler called Name
%% the handler function is no_op — so we do nothing with the event
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).
add_handler(Name, Fun) -> Name ! {add, Fun}.
%% generate an event
event(Name, X) -> Name ! {event, X}.

my_handler(Fun) ->
receive
{add, Fun1} ->
my_handler(Fun1);
{event, Any} ->
(catch Fun(Any)),
my_handler(Fun)
end.
no_op(_) -> void.

What I realized after looking at a few Erlang code snippets is that to convert this to Akka, we should do the following:

  • Read code from the bottom up
  • Extract actors first

So let’s begin by extracting the function my_handler into a dedicated actor. Scala code follows:

// EventHandler.scala
package
eventhandler
import akka.actor._
object EventHandler {
case class Add(function: Any=>Any)
case class Event(event:Any)
def apply():Props = Props(new EventHandler)
}
class EventHandler extends Actor {
import EventHandler._
def noOp(x:Any) = ()
override def receive = handler(_=>())
def handler(function: Any=>Any): Receive = {
case Add(fun) => context.become(handler(fun))
case Event(evt) => Try(function(evt))
}
}

So nothing fancy here, just two types of messages:

  1. Add(fun) — changes the function that actor will call on each event
  2. Event(evt) — applies the provided function to the given event

Notice that I made the types of the function very generic and the no-op function is pretty simple.

Now let’s look at the functions that create this actor and operate on it:

% event_handler.erl
%% make a new event handler called Name
%% the handler function is no_op — so we do nothing with the event
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).
add_handler(Name, Fun) -> Name ! {add, Fun}.
%% generate an event
event(Name, X) -> Name ! {event, X}.

First let’s tackle the make — this function simply creates an actor with a no-op handler:

% event_handler.erl
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).

The problem in Akka is that we can’t just create an actor out of nowhere. We need an actor system. However, we can cheat here a little and use an implicit actor system that the caller has to provide. Once we decide on this hack, the Akka version is pretty straightforward (this code goes into EventHandler object):

// EventHandler.scala
def
make(name:String)(implicit system:ActorSystem) = {
system.actorOf(EventHandler(), name)
}

Next let’s do the add_handler — this function looks up the actor with a given name in the registry and sends it a new function to use in the future:

% event_handler.erl
add_handler
(Name, Fun) -> Name ! {add, Fun}.
%% generate an event

Akka doesn’t have a registry (Erlang register keyword), but it does have actor selection, which allows us to query for an actor by name.

// EventHandler.scala
def
addHandler(name:String, function:Any=>Any)
(implicit system: ActorSystem) = {
system.actorSelection("/user/" + name) ! Add(function)
}

Here we cheat again by requiring an actor system to be present (see calling these functions below), and we use actor selection in Akka to provide functionality similar to that of a registry.

Lastly there is the event — function that actually sends the event to our event handler:

% event_handler.erl
event
(Name, X) -> Name ! {event, X}.

We pretty much copy what we did for addHandler only we send a different message this time:

// EventHandler.scala
def
event(name:String, x:Any)(implicit system:ActorSystem) = {
system.actorSelection("/user/" + name) ! Event(x)
}

Let’s run it

Let’s run our system so far. What we want to see is… nothing. At least no errors. Here is how we setup the Erlang version in the REPL:

% Erlang REPL
1> event_handler:make(errors).
true

2> event_handler:event(errors, hi).
{event,hi}

In Scala I’m going to create a main app instead of using the REPL, just so we can re-run it in case there is a mistake. Here is how our app would look so far:

// Main.scala
package
eventhandler

import akka.actor.ActorSystem

object Main extends App {
import MotorController._
implicit val system = ActorSystem()

EventHandler.make("errors")
EventHandler.event("errors", "hi")
}

Notice a few things about the Scala code:

  1. We create an actor system and mark it as implicit. This is needed by functions make and addHandler in the EventHandler object.
  2. The invocation of make and event look almost the same as the Erlang counterparts.
  3. Running it produces no output… this is because Akka’s send operator does not return anything. Unlike Erlang which returns the argument. So Name !{event, X} in Erlang would return {event,X}, which is why we see {event, hi} in the Erlang’s session.

Motors

The final piece of the puzzle is actually writing an event handler to be installed with the given actor. In Erlang this looks as follows:

% motor_controller.erl 
-module(motor_controller).
-export([add_event_handler/0]).

add_event_handler() ->
event_handler:add_handler(errors, fun controller/1).

controller(too_hot) ->
io:format(“Turn off the motor~n”);

controller(X) ->
io:format(“~w ignored event: ~p~n”,[?MODULE, X]).

Here there is no actor, only api functions that allow the user to call add_event_handler from this module and automatically received new behavior for the actor named errors. In fact the whole thing about actors is completely hidden from the user. We follow this good example in Scala, translating the word module to object and create the following:

% MotorController.scala
package
eventhandler
import akka.actor.ActorSystem

object MotorController {
case object TooHot
def addEventHandler()(implicit system: ActorSystem) = {
EventHandler.addHandler("errors", controller)
}
def controller(evt:Any) = {
evt match {
case TooHot => println("Turn off motor.")
case _ => println(s"Ignored event: $evt")
}
}
}

Couple of things to note about this implementation are:

  • We use the Scala’s pattern matching to compensate for the lack of equivalent Erlang syntax
  • We take advantage of the case objects to define the TooHot message
  • We simply call addHandler function on EventHandler object — this looks identical to Erlang code!
  • We have to require an implicit actor system in addEventHandler since addHandler requires it

We are now done with conversion! Let’s run it!

Run it again

Now that the whole is complete, let’s see how to put it all together. Here is how it should behave according to Erlang’s implementation:

% Erlang REPL continued...
3> c(motor_controller).
{ok,motor_controller}

4> motor_controller:add_event_handler().
{add,#Fun<motor_controller.0.99476749>}
5> event_handler:event(errors, cool).
motor_controller ignored event: cool
{event,cool}
6> event_handler:event(errors, too_hot).
Turn off the motor
{event,too_hot}

Now here is the equivalent main app:

// Main.scala
package
eventhandler

import akka.actor.ActorSystem

object Main extends App {
import MotorController._
implicit val system = ActorSystem()

// Create an event handler and generate an error
EventHandler.make("errors")
EventHandler.event("errors", "hi")
MotorController.addEventHandler()
EventHandler.event("errors", "cool")
EventHandler.event("errors", TooHot)
}

and it’s execution:

Ignored event: cool
Turn off motor.

Things Learned

  1. Reproducing Erlang’s event handler pattern seems possible with Akka
  2. Api pattern is really nice — in Scala we define an companion object and use it to present a nice synchronous api to the user.
  3. Actor selection possible use case— I never needed or could come up with an actual valid use case for actor selection before.
  4. Event handler pattern — we can now implement our own logger with this! Imagine changing the logging level of the system on the fly, by sending it a no-op function!
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.