Scala in practice: Composing Traits – Lego style

As a kid, i loved to play with Lego bricks, especially to build freaky spacecrafts.

spacecraftAt that time it was easy to let my phantasy go (where noone has gone before) and build completely new models simply by composing some standard bricks. Those bricks weren’t too specialized, meaning that there weren’t too many constraints on how to combine them. On the other side you always had to compose a new spacecraft from the very ground up as there weren’t some more higher organized units like engines or control cabins.

Nowadays, you’ll find such units. There are engines, control cabins or a whole commando bridge, Wings, Field Generators and so on – a whole set of higher organized units whithin a single domain. On the other side, you can’t combine every unit with an arbitrary other unit within that domain since there are some ‘constraints’  that will prohibit some unsound combinations.

Now you may ask how that cute childhood story relates to Scala?
You may have seen already some similarities to the field of software development where you also want to compose some higher organized building blocks within a certain domain, enforcing that they are only combined in a proper way. It turns out, that Scala’s concept of traits provide some mechanisms of ‘mixin’ them together while enforcing the compliance of some constraints. This may sound too abstract at that point, but hold on – we’ll build some spacecrafts in the above mentioned way and things will get clearer.

Look, it’s a spacecraft

Let’s start with a hull for our spacecraft. To keep things simple and to focus on the core idea of constrained
composition, the spacecraft’s hull will only provide one abstract method engange to start the craft:

abstract class Spacecraft{

    def engage
}

As you can see, method engage and therefore the whole class is abstract, so you can’t instantiate a pure hull
of a spacecaft. So whenever we want to build a full fledged craft, we may have to ‘add’ (or mix in) a component
that knows how to engage that craft.

Captains place

Typically, a spacecraft possess a kind of ‘control center’ which is normally well suited for initiating the
start of a craft, hence should provide an implementation for method engage. There may be different kinds of
control centers that could be used for building your own, customized spacecraft – e.g. a whole commando bridge for
those big deep space crafts or a small control cabin for those little crafts mainly maneuvering near the orbit.

 trait CommandoBridge{

def engage { for( _ <- 1 to 3 ){ speedUp } }

    def speedUp
}

Now we see what it means to engage (a Spacecraft) if composing a commando Bridge to the hull. We simply speed up that craft 3 times.
But hold on – although we know what it means to engage that craft, speeding up that craft is not in the responsibility of the commando bridge, since speedUp is left abstract (speeding down is omitted since it follows the same mechanism – you get the idea).

Re-calibrating all Dilithium crystals

So the spacecraft seems to be incomplete without a unit to speed it up – let’s call such a unit ‘engine’.  Again, there may be different kinds of engines we could select from to assemble our craft.
Let’s say there is a Pulse-Engine that directly supports the command of speeding up:

trait PulseEngine{

    val maxPulse: Int

    var currentPulse = 0;

    def speedUp { if( currentPulse < maxPulse ) currentPulse += 1  }
}

As you can see, a PulseEngine is able to speed up until a maximum pulse rate. In order to ‘produce’ different pulse engines (supporting different maximum pulse rates for different types of crafts), the field is again left abstract.
Now we could create our first spacecraft, using a commando bridge and a pulse engine (let’s say that’s all you need for building a full fledged spacecraft).

class StarCruiser extends Spacecraft with CommandoBridge with PulseEngine{

    val maxPulse = 200
}

As you can see, we’ve created a new (Sub-)Type of a spacecraft and mixed in both Traits, obtaining a commando bridge (that knows how to engage the whole craft) and an engine (that knows how to get the craft into speed when engaging the craft).
The only thing left is to define the maximum pulse rate our StarCruiser is able to achieve.

Wiring

In the above case, all units fitted together smoothly. For example, a pulse engine provided exactly the ‘interface’ (speedUp) that was needed by a commando bridge, so you could compose both without additional work. Let’s take a look at another control center, that we could apply to our craft, that may offer an incompatible ‘interface’ :

trait ControlCabin{

    def engage = increaseSpeed

    def increaseSpeed
}

This time we need to do some additional wiring, if we want to compose a new type of craft using a control cabin and a pulse engine, since both units don’t fit together directly (the dependency needed by ControlCabin (increaseSpeed) isn’t directly fulfilled by PulseEngine)

class Shuttle extends Spacecraft with ControlCabin with PulseEngine{

    val maxPulse = 10

    def increaseSpeed = speedUp
}

As you can see, we have to wire together the control cabin with the pulse engine in order to let them cooperate.
In the same way, we could think of another kind of engine which offers a completely different ‘interface’:

trait WarpEngine extends Engine{

    val maxWarp: Int

    var currentWarp = 0;

    def toWarp( x: Int ) { if( x < maxWarp ) currentWarp = x }
}

Again, we need to wire together the concrete control center with the WarpEngine, depending on their incompatible ‘interfaces’.

Let’s compose a craft, using a commando bridge and a warp engine.
Firstly, we are forced to define a maximum warp level, since it’s an abstract field of WarpEngine. Secondly we have to wire together the commando bridge with the warp engine, that is to ‘route’ the commando bridge’s method speedUp to the warp engines ‘interface’ toWarp with an appropriate implementation:

class Explorer extends Spacecraft with CommandoBridge with WarpEngine{

    val maxWarp = 10

    def speedUp = toWarp( currentWarp + 1 )
}

Alternatively, we could also use a simple control cabin for another type of spacecraft. Again we have to link the contol cabins commands (increaseSpeed) to the warp engines ‘interface’:

object Defiant extends Spacecraft with ControlCabin with WarpEngine{

    val maxWarp = 20 // claimed by WarpEngine

    def increaseSpeed = toWarp( 10 ) // claimed by ControlCabin
}

Restricted Access

Until now, we only applied a control center or engines to spacecrafts. But nothing would restrict us to use those units in other domains so far. Say we want to build a certain airplane and apply a warp engine.

class Jet extends Airplane with WarpEngine{

    val maxWarp = 5
}

It’s propably not the best idea to equip a Jet with a warp engine, since this seems to be a bit oversized for an airplane. We need a way to restrict the usage of warp engines – they should be only applied to spacecrafts. Fortunately we can express this kind of constraint, using Scala’s self type annotation. Included within a trait, it’s like saying ‘this trait is only allowed to be mixed into a type of x‘ (in our case ‘Spacecraft‘):

trait WarpEngine extends Engine{

    this: Spacecraft =>
    ...
}

As you can see, we used the WarpEngines self type to restrict its appliance only to spacecrafts. In all other cases, Scala’s compiler will complain about an unsound mixin.

What makes a spacecraft a spacecaft ?

With selftypes, we now have an instrument to restrict the usage of a trait to be mixed in only to a certain Type.
On the other side, we aren’t forced to use a control center or an engine at all if creating a new spacecraft, since we could provide an implementation of the spacecrafts abstract methods directly within a subtype. That may be fine in some cases, but what if we want to state that a spacecraft has to be composed of at least a certain type of control center and a certain type of engine? Again, we can use the service of the self type annotation, this time applied to our abstract class spacecraft, stating that a spacecraft should at least be compound of a control center and an engine:

abstract class Spacecraft{

    this: ControlCenter with Engine =>
    ...
}

The only thing left is to provide an appropriate type ControlCenter resp. Engine and the correct classification of those concrete units (e.g. ‘CommandoBridge is a ControlCenter‘)

 trait ControlCenter

 trait CommandoBridge extends ControlCenter{ ... }

 trait ControlCabin extends ControlCenter{ ... }

 trait Engine

 trait PulseEngine extends Engine{ ... }

 trait WarpEngine extends Engine{ ... }

Summary

Abstract methods and self type annotations are two powerful tools which help to guide or constrain the composition of traits.
You may use abstract methods and abstract fields to enforce a kind of ‘wiring’ between multiple units or at least to force the definition of some concrete information.
You may use a self type annotation to restrict the appliance of a trait, so that it can only be mixed in to a certain type (or subtypes). On the other side, you’re able to enforce that a certain trait (or subtype) have to be mixed in to a certain type, again by using a self type annotation.
In all cases, the ‘composer’ of those units will be guided by the compiler – you can’t forget to give a definition for an abstract method or arrange an unsound composition, since all those ‘constraints’ are based on Scala’s statically typed Type system.

Advertisements

7 Responses to “Scala in practice: Composing Traits – Lego style”

  1. Twitter Trackbacks for Scala in practice: Composing Traits – Lego style « brain driven development [gleichmann.wordpress.com] on Topsy.com Says:

    […] Scala in practice: Composing Traits – Lego style « brain driven development gleichmann.wordpress.com/2009/10/21/scala-in-practice-composing-traits-lego-style – view page – cached At that time it was easy to let my phantasy go (where noone has gone before) and build completely new models simply by composing some standard bricks. — From the page […]

  2. Stephan Schmidt Says:

    What an excellent post. Gained some insights, for the first time really understood self types. Looks like Java could do many things in DDD for which Java needs a framework like Qi4J.

    Cheers
    Stephan
    http://codemonkeyism.com

  3. Scala in practice: Composing Traits – <b>Lego</b> style « brain driven <b>...</b> Says:

    […] 1 votes vote Scala in practice: Composing Traits – <b>Lego</b> style « brain driven <b… Scala in practice: Composing Traits – Lego style. October 21, 2009 — Mario […]

  4. Mario Gleichmann Says:

    Stephan,

    many thanks for your nice feedback!

    In fact i also had to think about Qi4Js mantra of ‘Composite Oriented Programming’ when hearing the first time of Scala’s possibilities to use traits as mixins.

    Greetings

    Mario

  5. Top Posts « WordPress.com Says:

    […] Scala in practice: Composing Traits – Lego style As a kid, i loved to play with Lego bricks, especially to build freaky spacecrafts. At that time it was easy to let my […] […]

  6. Lance Woodson Says:

    Mario, thanks for the scala articles. I find them very helpful in wrapping my head around the language and its myriad of features. One thing occurred to me while reading this one, however. Wouldn’t a spaceship model be better handled through composition? I mean, a spaceship has a control center and an engine, but it isn’t a control center and an engine. I might make engine and control center classes, and define HasEngine & HasControlCenter traits that organize their interaction. Dunno, this may be ugly for other reasons.

  7. Functional Scala – Mario Gleichmann « Another Word For It Says:

    […] Scala in practice: Composing Traits – Lego style […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: