Traits, Multiple Inheritance, and Actors in Scala

by Kevin Hoffman on July 19th, 2011

I’ve been experimenting with traits, multiple inheritance (mixins), and Actors in Scala and I’ve found it difficult to merge all 3 aspects of the language. If you’ve been reading any of my posts that I’ve written while attempting to build a MUD, you may have noticed that the architecture is starting to get a little muddy when it comes to having multiple message handlers.

Rather than use a MUD as an example, I want to use an even simpler example to make my problem easier to describe. Let’s say I want to create an Actor. This actor is highly overworked and as such, he is the chief cook and bottle washer. Cooks can respond to the CookThis message. Bottle washers can respond to the WashThis message. The chief cook and bottle washer can also respond to a message instructing him to DoWork.

Constantly in search for an elegant, simple, and clean solution to this problem I immediately think about trying to solve it using traits. My first stab at a solution looks like the code below, where I’ve created Traits for Cook and Bottlewasher and the main Actor also has its own message handling logic:

Logically, if Traits gave us true multiple inheritance, we would expect that when we run this application we will see three messages as our overworked Actor does everything from coding to washing plates to cooking hot dogs. What we actually see is the Actor responding only to the DoWork message.

This is because of how Traits are actually used to generate the underlying code that belongs to container classes within the JVM. What’s happening is that each trait is overriding the act() function and the Overworked class is also doing an override. Because this class is the outermost parent in a hierarchy of contained classes (as seen by the JVM, not by the Scala programmer), it makes the other act() implementations vanish.

What can I do to fix this? What if I use some other method and call it from act(), adopt the abstract superclass style? The problem is this: if the alternate method has the same name in every trait, that method will suffer from the same vanishing that we saw with the original solution and our overworked Actor won’t respond to all of the appropriate messages.

One option is to make the outermost containing Actor handle all of the messages and forward them to the trait implementations via trait-specific methods like this:

This solves one problem but creates another. We’re now able to arbitrarily mix in traits that provide functionality that should be invoked in response to an incoming message, but now we have an SRP (Single Responsibility Principle) and LSP (Liskov Substitution Principle) failure in our design. Our Overworked class needs explicit knowledge of every single message that each trait should respond to and how it should respond. This means that every time any of the traits is expanded to handle new messages, every single class with that trait must also modify its pattern matching inside the act() method!

So now what can we do? At this point I felt like I was so close, I knew that Scala could handle this problem in an elegant way and, as it turns out, I was right. I refactored my initial solution to one that was much smaller. I gave each trait it’s own message handler method (to avoid name-collision-driven method vanishing) and then chained them together using the orElse function, like this:

There are a couple of really powerful things happening here this time. First, you may have noticed that my new traits no longer extend Actor - meaning that they don’t have to be Actors to process messages, allowing the traits to be mixed into any class. Next, the individual message handling methods are protected, which makes it obvious to anyone reading the trait that it’s perfectly OK to create derived child traits with their own message handlers and still invoke the base/parent message handler.

Finally, the orElse function lets me chain all of the trait message handlers together. While the Overworked actor still has to know the name of the message handler function from the mixed-in trait, a simple naming convention of handle[TraitName]Messages makes this easily taken care of. Now, rather than bleeding responsibility out of the traits and into the parent containing class, all the real Actor needs to know is which traits are being mixed in.

The only visible drawback here is that if you have two traits that both want to handle the same message, the trait whose message handler appears earlier in the orElse chain will win and the other trait will never receive the message.

There may be a way to simplify this even more but as it is, I really like this pattern and it allows me to mix in arbitrary message handlers to any Actor. My next step will be go to back into my MUD code and refactor it all to clear up roles and responsibilities.

Derek,
Thanks for the comment. I’ve spent a little bit of time looking at Akka Actors but I haven’t had a chance yet to actually fire up an Akka grid and enjoy the clustered, remote Actor love.
One impression I remember having the first time I skimmed the Akka docs was that the “become” and “unbecome” functionality in Akka’s (lighter weight than Scala’s) Actors was quite possibly one of the coolest things I’d seen since the invention of cookie dough ice cream!

Thanks!

Derek Wyatt

I use the become feature a fair bit, and I can generally agree that it’s pretty sweet. Specifically I have a “Gate” class that changes its internal running state from Open / Connected to Open / Disconnected to Closed. ‘become’ is awesome for this….

However The very nature of ‘become’s existence basically encourages the notion of a stateful actor, and we all know how interesting that is… In fact, it is so problematic that I pushed for an enhancement to the ActorRef that allowed for a specific restart factory that lets you resurrect your actor in its previous (as opposed to its initially constructed) state, assuming that it would be safe to do so.

The Akka guys agreed and are putting the finishing touches on it now, I believe. Those guys absolutely kick ass