Clojure and Akka: A match made in …
Akka has been in the news lately. It’s an actor framework, STM, etc rolled into one and generally joined at the hip with Scala. Despite that, it does have a Java API, so after seeing an interesting Akka talk by Nilanjan Raychaudhuri at Detroit Cloud Dev Day over the weekend, I thought I’d try using it from Clojure. Why? Because it’s there!
Now, Clojure already has pretty equivalent concurrency constructs built into the language, so the utility of this little adventure is a little suspect. But Clojure’s constructs are strictly local, so Akka’s support for distributed actors keeps it interesting (*).
Anyway, after an evening with Clojure and Akka, I have to say they weren’t made for each other. I decided to do a straight port of the Java-based Pi calculation example from the Akka tutorial. I figured porting from Java to Clojure would be simpler than Scala to Clojure. It turns out that Akka, at least in the example, makes heavy use of sub-classing and further more the class literals of those sub-classes. In particular, you need a class literal to create a new instance of an actor. You can’t just “new it up” on your own. Now, Clojure has
(proxy) which will let you extend Java classes just fine, but it creates a new instance of an anonymous class. It doesn’t really give you a class to work with. There’s also
(gen-class), but I wanted to see if I could avoid that for the time being.
As luck would have it, Akka also supports actor factories in addition to the class literal approach, so I was able to add an additional layer of proxy fun and get the effect I wanted. Wrap it in a macro and you get something like this:
(defactor worker   (onReceive [work] (let [result (calculate-pi-for (:start work) (:num-elements work))] (.. this getContext (replyUnsafe (Result. result))))))
and then use it like this:
Not beautiful by any stretch, but ok for an evening experiment.
One interesting “feature” of Akka that I stumbled upon is an error message like this:
You can not create an instance of an actor explicitly using 'new MyActor'. You have to use one of the factory methods in the 'Actor' object to create a new actor. Either use: 'val actor = Actor.actorOf[MyActor]', or 'val actor = Actor.actorOf(new MyActor(..))'
Well, I was using factories throughout, but it turns out that the way this is enforced is with a thread local “current actor under construction” variable which apparently doesn’t play nicely if actor creation is nested within factories. Once I flattened out my actor creation, the error went away. Here’s the output from a run (full of warnings because I didn’t bother with actually installing Akka and just used Maven to get the jars):
61 [21:29:42] [~/Documents/projects/clojak]$ lein run -m clojak.pi Can't load 'akka.conf'. One of the three ways of locating the 'akka.conf' file needs to be defined: 1. Define the '-Dakka.config=...' system property option. 2. Put the 'akka.conf' file on the classpath. 3. Define 'AKKA_HOME' environment variable pointing to the root of the Akka distribution. I have no way of finding the 'akka.conf' configuration file. Using default values everywhere. Pi estimate: 3.1435501812459323 Calculation time: 11841 millis
In conclusion, here are my observations from this experience:
- My conversion of the PI calculation function to Clojure is an unreadable atrocity.
- It’s probably worth investigating why this is more than 10x slower than the sample output in the tutorial.
- (*) Although I got it working with actor factories, this evidently makes distributing the actors difficult/impossible, which kind of eliminates any benefit that Akka would bring to Clojure.
- By resorting to
(gen-class), I should be able to generate “real” classes in Clojure for use by Akka. A simple stubbed implementation that delegates to a map of functions should make it pretty easy to use. I did something similar in Seesaw’s nascent applet support.
- Clojure’s Java interop is generally awesome, but it works better with some API styles than others.
There’s a good chance that I just don’t know what I’m doing, so feel free to correct any egregious mistakes.
Ok. That’s it. Source code’s here.