Friday, March 20, 2009

Cloning Scala actors

I thought I'd base one of my projects on Scala actors. On the other hand seeing how actors are dynamically typed reminded me of my experiments with dynamic, clone based languages (Io, Ioke, Self, JavaScript).
The preferred way to create an actor in Scala is something like this:


val anActor = actor {
// here goes the body of an actor.: a loop, call to receive/react, message patterns...
}

The problem is I want to have a function cloneActor() that would return a clone of a scala actor:

val anotherActor = cloneActor(anActor)

Note that (for now) I don't want this actor's state to be cloned, just the body.
Just like if you would clone a human being: genetically identical body, but a fresh, clear memory...
At first I thought about using reflection in order to create new instance of the existing actor.



val reflectivelyClonedActor = anActor.getClass.newInstance // this won't work

Even if it didn't throw a run-time exception it wouldn't clone the body of the actor. :-(
The body is a closure passed to the scala.actor.Actor.actor() method.
Second idea was to just create a new actor in a standard way, but reflectively copy the body... Unfortunately the body isn't kept in a explicitly declared field.
Althought there is a implicitly created synthetic field body$1, it's marked private final by the compiler.

scala> a1.getClass.getDeclaredField("body$1").get(a1)
java.lang.IllegalAccessException: Class can not access a member of class scala.actors.Actor$$anon$1 with modifiers "private final"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at .....

Well it was too tricky in the first place... OK. Out of ideas. Let's look at the source of scala.actor.Actor:

def actor(body: => Unit): Actor = {
val actor = new Actor {
def act() = body
}
actor.start()
actor
}

Interesting thing is that act() has type () => Unit while body has type => Unit.
So we can freely use existing actor's act as a body of new actor. Voila!


def cloneActor(a: Actor) = actor(a.act)

Sure it has it's downsides...
What's more interesting if you want to treat actor as wrappers for threads to use all 8 cores of your shiny new machine:

implicit def intmulact(i: Int) = new {
def *(a: scala.actors.Actor) = (1 to i) map { _ => cloneActor(a) } toList
}

val executors = 8 * actor {
// body of the actor
}

2 comments:

  1. just an as an FYI, (i like your final solution better) you can get around private in reflection using setAccessible = true

    http://www.exampledepot.com/egs/java.lang.reflect/SetAccessible.html

    ReplyDelete
  2. Thanks... I haven't programmed much Java before learning Scala... :-)

    ReplyDelete