∞ Aqua Teen Hunger Force colon Movie for Theaters
Equal parts baffling and hilarious. I’ve not seen the TV show so I had to absorb all of the characters and running jokes in a single barrage of craziness.
| samskivert » 2009 » June |
June 26, 2009∞ Aqua Teen Hunger Force colon Movie for TheatersEqual parts baffling and hilarious. I’ve not seen the TV show so I had to absorb all of the characters and running jokes in a single barrage of craziness.
June 17, 2009∞ A technique for type-safe, concise actors in JavaActors are great. They provide a concurrency model that is not too taxing to reason about and an organizing principle that tends to jive with how you would naturally structure your code. I recently put actors to use in a real project (implemented in Java), and in the process had to overcome some aesthetic difficulties with a technique that seemed worth sharing. The Itch The requirements I was trying to satisfy are as follows (note: I realize that most people use the term messages to describe what actors process but I prefer to say that actors process actions, so hopefully you can bear with my rogue nomenclature):
I am using the Functional Java actor framework, which provides a QueueActor that implements the actor semantics I desire given an ExecutorService. Using the QueueActor is pretty straightforward:
class Action { /* some action class */ }
Strategy<Unit> strategy = Strategy.executorStrategy(someExecutor);
QueueActor<Action> actor = QueueActor.queueActor(strategy, new Effect<Action>() {
public void e (Action action) {
// handle your action, blissfully ignore concurrency
}
});
actor.act(new Action(...));
However, you can imagine that using this building block to meet my requirements was going to require some effort. After trying and discarding a number of unsatisfactory approaches involving large quantities of boilerplate and/or a lack of type safety, I eventually arrived at a technique that avoids both. The key is to use generics and Java’s wee bit of type inference to our advantage. The Scratch To see how this all hangs together, let’s start by looking at an implementation of a basic actor using the technique. As you can see below, we have created an AbstractActor class and our actor implementations derive from that.
public class Thingy extends AbstractActor<Thingy>
{
/** Instructs the thingy to initialize itself.
* Param: File - the directory in which the thingy operates. */
public static final Action1<Thingy, File> INIT =
newAction(Thingy.class, "init", File.class);
/** Instructs the thingy to shut itself down. */
public static final Action<Thingy> SHUTDOWN =
newAction(Thingy.class, "shutdown");
public Thingy (Strategy<Unit> executor) {
super(executor);
}
/** Handles the {@link #INIT} action. */
protected void init (File dir) { ... }
/** Handles the {@link #SHUTDOWN} action. */
protected void shutdown () { ... }
}
To send this actor an action, we do the following: thingy.act(Thingy.INIT, new File(somedir)); thingy.act(Thingy.SHUTDOWN); Going back to my requirements, we can see that a few of them are already in good shape:
The Gory Details Let’s take a look behind the curtain and see how it works. First let’s look at what newAction() does:
public abstract class AbstractActor<T extends AbstractActor>
{
// ...
protected static <A extends AbstractActor> Action<A> newAction (Class<A> clazz, String name) {
return new Action<A>(resolveMethod(clazz, name));
}
protected static <A extends AbstractActor, A1> Action1<A, A1> newAction (
Class<A> clazz, String name, Class<A1> arg1) {
return new Action1<A, A1>(resolveMethod(clazz, name, arg1));
}
// ...
}
Here’s where we start to get into some type gymnastics. newAction() returns an Action class (or subclass) parameterized by the types of the actor class and the arguments to that action. So when we call newAction(Thingy.class, “init”, File.class) above, we get back an Action1<Thingy, File> object. This is critical to propagating the type information to the act() method and enforcing the correct types and count for the action arguments. Let’s take a look at act() now:
public abstract class AbstractActor<T extends AbstractActor>
{
// ...
public void act (Action<T> action) {
enact(action);
}
public <A1> void act (Action1<T, A1> action, A1 arg1) {
enact(action, arg1);
}
// ...
}
There are a couple of things going on here. First, you can see that AbstractActor is parameterized in the type of itself, or rather of the type of the class that extends abstract actor. So we have Thingy extends AbstractActor<Thingy>. This then requires that all act() methods take Action objects that are typed Action<Thingy> and makes it a compile error to supply an Action typed for some other AbstractActor derived class. To enforce the correct number of arguments, we simply have unique Action derivations for every arity. Action for zero-argument methods, Action1 for one-argument methods, and so on. In the examples I’ve only provided guts for zero and one argument actions, but you can trivially extend this to as many arguments as you desire. Finally to enforce the type of the arguments, we use Java’s limited, but useful, generic type inference. By declaring our second act() method with type <A1>, which is inferred from the type of the Action1<T, A1> that is supplied when you call the method, we define A1 via the first parameter and the compiler requires that the subsequent “A1 arg” parameter match. So the type that was provided back in our newAction() call is properly preserved and enforced any time that Action is passed to an act() method. Note that I’m glossing over argument covariance here for clarity. What you really want is:
public <A1, B1 extends A1> void act (Action1<T, A1> action, B1 arg1) {
This allows you to declare a method that takes Number, for example, and pass an Integer to it. Java methods are covariant in their argument’s primary types, so we probably want actor actions to be the same. Now let’s look at how we wire up and dispatch the action implementation methods internally. As you may suspect, we use reflection to do this. Normally one gives up compile time type checking when making use of reflection, but we’ve fortunately already handled that. One downside is that reflective method invocation is slower than a non-reflective method invocation. However, later I’ll show you how you can do away with the reflective dispatch for specific situations where you’ve discovered that performance is actually critical. This comes at the cost of some boilerplate which is why we prefer the reflective dispatch by default. Now then, back to the implementation. You’ll notice that newAction() above calls a method “resolveMethod()” and passes its results into the Action constructor. This method is pretty simple. newAction() takes the name of the method and the types of its parameters, and Class provides a mechanism to look up a method given that exact information. We’d just call it directly except that unfortunately NoSuchMethodException is a checked exception.
public abstract class AbstractActor<T extends AbstractActor>
{
// ...
protected static Method resolveMethod (Class<?> clazz, String name, Class<?>... ptypes) {
try {
return clazz.getDeclaredMethod(name, ptypes);
} catch (NoSuchMethodException nsme) {
throw new IllegalArgumentException("Unable to resolve actor method: " + name, nsme);
}
}
// ...
}
There’s another downside to using reflection. We specify the name of the reflected method to be called as a String passed to newAction(). What if you misspell that? What if you pass the argument classes in the wrong order? Since newAction() is called to initialize static fields, it will be called as soon as the Thingy class is resolved. This means that as soon as the Thingy class is referenced (a class that invokes actions on Thingy is resolved, or an instance of Thingy is created, etc.), the programmer finds out that they made a mistake in an action declaration. This is not delayed until the program actually sends the action in question to the actor. So while the class still has to be loaded, which could conceivably not happen immediately upon program startup, we’re still very likely to discover any problems as soon as possible during the development process. The remainder is pretty simple. The Action class simply reflectively invokes the method when it is acted upon and AbstractActor’s enact() method simply queues up a Runnable to invoke the Action using appropriate actor semantics. I’ll include the entirety of AbstractActor here so that you can see how it all hangs together.
import java.lang.reflect.Method;
import fj.Effect;
import fj.Unit;
import fj.control.parallel.QueueActor;
import fj.control.parallel.Strategy;
public abstract class AbstractActor<T extends AbstractActor>
{
public void act (Action<T> action) {
enact(action);
}
public <A1> void act (Action1<T, A1> action, A1 arg1) {
enact(action, arg1);
}
protected void enact (final Action<T> action, final Object... args) {
_actor.act(new Runnable() {
public void run () {
@SuppressWarnings("unchecked") T self = (T)AbstractActor.this;
action.invoke(self, args);
}
});
}
protected AbstractActor (Strategy<Unit> execor) {
_actor = QueueActor.queueActor(execor, new Effect<Runnable>() {
public void e (Runnable action) {
action.run();
}
});
}
protected static <A extends AbstractActor> Action<A> newAction (Class<A> clazz, String name) {
return new Action<A>(resolveMethod(clazz, name));
}
protected static <A extends AbstractActor, A1> Action1<A, A1> newAction (
Class<A> clazz, String name, Class<A1> arg1) {
return new Action1<A, A1>(resolveMethod(clazz, name, arg1));
}
protected static Method resolveMethod (Class<?> clazz, String name, Class<?>... ptypes) {
try {
return clazz.getDeclaredMethod(name, ptypes);
} catch (NoSuchMethodException nsme) {
throw new IllegalArgumentException("Unable to resolve actor method: " + name, nsme);
}
}
protected static class Action<A extends AbstractActor> {
public Action (Method method) {
_method = method;
}
public void invoke (A actor, Object... args) {
try {
_method.invoke(actor, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Method _method;
}
protected static class Action1<A extends AbstractActor, A1> extends Action<A> {
public Action1 (Method method) {
super(method);
}
}
protected QueueActor<Runnable> _actor;
}
The Fine Print I mentioned above that I’d indicate how to avoid the reflective call. You can probably figure this out yourself now that you’ve seen all the code, but just in case you’re conserving brain power for more important projects, I’ll show you an example of how to do that here:
public class Thingy extends AbstractActor<Thingy>
{
public static final Action1<Thingy, String> ZIP = new Action1<Thingy, String>(null) {
public void invoke (Thingy actor, Object... args) {
actor.zip((String)args[0]);
}
};
/** Implements the {@link #ZIP} action. */
protected void zip (String zoom) { ... }
}
Note that you don’t have to worry about checking that args has one argument or that the argument is a String, because the compiler is ensuring that you can only call act(Thingy.ZIP, …) with a single string argument. It is worth noting that if you want primitive arguments you have to worry about both boxing and unboxing and that someone could do something stupid like: actor.act(Thingy.FOO, (Integer)null), but fortunately that brand of stupidity usually doesn’t happen accidentally. You also might be wondering about actors sending responses to other actors and callers magically blocking until they receive their response. Functional Java’s actor library doesn’t support blocking on a response from an act() call, so that’s out. I don’t think it would be incompatible with this technique were it to be supported. In terms of sending responses at all: you could pretty easily pass a concrete actor to another actor and have that actor send an action in response. The trickier thing would be if you wanted to support sending a response to, say, any actor that supported the appropriate response action. Without having actually tried it, I think it would probably be possible using some clever definition of interfaces and more type gymnastics. I’ll leave that as an exercise for the reader or for myself and a future blog post.
June 14, 2009∞ iPeggleI discovered recently that Peggle is available on the iPhone for $1 and I am simply appalled. I don’t know what to think about a universe where one can obtain, for a single US dollar, a quantity of awesome in proportions so epic as to tax the imagination. It spits in the face of the very notion of value. Next they’ll be selling Ferraris for $7.50 and caviar for $.25 a pound. The singularity must be nigh.
June 13, 2009∞ MetoroporisuOsamu Tezuka’s character design kind of creeps me out. The beady eyes and the giant Popeye legs are very disturbing when plopped down in the middle of an immaculately rendered Art Deco dystopia. In spite of that, the visual experience was incredible and the film dipped into anime cliché with merciful infrequence.
June 7, 2009∞ The WrestlerThis movie gives Requiem for a Dream a strong challenge for the Most Depressing Film championship. Micky Rourke looked like life had chewed him up and spit him out. This film (and a few other contemporary works) seems to me to be a new take on tragedy. It’s not Shakespearean — at no point do you feel like the characters are making epic mistakes — life just sucks from the start to the finish and the world relentlessly beats the crap out of some poor downtrodden bastards until you can barely stand to watch it any longer and then the movie just ends.
∞ Drag Me to HellEqual parts terror, hilarity, gross-out and quality filmmaking. This is the best thing to happen to fortune tellers in at least a couple of generations. I did struggle with the fact that Justin Long approaches Keanu Reeves levels of annoying, but I managed to mostly ignore his smarmy boyfriendness.
June 4, 2009∞ Dan In Real LifeI can’t dissociate Steve Carell from his role as Michael Scott in The Office. That didn’t totally ruin the movie for me, but it did make me expect to cringe any time something even remotely socially awkward was about to take place. He’s probably doomed to nothing but voice overs for animation as far as I’m concerned.
June 2, 2009∞ Hors de prixIdle amusement, kooky Frenchness, and Audrey Tautou being hot instead of mousy.
|
Bits Ludography Reviewlets Camera Wrestling
2009/01 - Brazil, Argentina
2008/08 - Burning Man 2008/06 - Trans-Siberia 2006/12 - New Orleans 2004/08 - Burning Man 2004/04 - Nepal Himalayas 2004/03 - Kamakura 2004/02 - Mitake-san 2002/07 - 日本 2001/12 - Jamonduras 2001/08 - Burning Man 2001/04 - Italy 2000/12 - Morocco 2000/09 - New Zealand 2000/08 - Burning Man 2000/06 - Chickens! 1999/12 - Fiji Archives: | ||||||||||
| samskivert.com | ©2001 - 2009 Michael Bayne <mdb@samskivert.com> |