Monday 5 January 2009

Scala, traits and mix-ins

Continuing on in the series of Scala blogs, showing Scala features by example, this post attempts to show the operation of Traits. Traits are rather like classes but designed to allow rich feature composition by allowing "mix-in" of required traits dynamically, at run time. Traits achieve what can look like multiple inheritance, without the pitfalls and limitations of multiple inheritance. Conversely then, you might think traits are equivalent to Java's interfaces. Whist similar, the key difference is that traits can (and usually do) contain implementations (method bodies). When a trait method implementation makes a call to super, it is dynamically resolved as the class is linearised.

When traits are combined with classes to create extended behaviour, this process is known as mixin and the traits are said to be mixed-in or mixins.

The example below shows the behaviour of the plain traits class using its putMsg method, and then its behaviour when mixed-in with TraitAddA, TraitAddA and TraitAddB and then with TraitAddA and TraitAddB reversed (ordering of the with parts is significant).

As well as mixin with the new keyword, class TraitABClass shows that traits can be used as mixins with normal classes too, at the point they are declared using the with keyword.


Example Scala file showing the operation of Traits:

package test

object TraitsTest {

def main(args : Array[String]) {

val traits : Traits = new Traits()

traits.putMsg("Hello!")

println("traits, value = " + traits.toString)

val traitsA : Traits = new Traits() with TraitAddA

traitsA.putMsg("Hello!")

println("traitsA, value = " + traitsA.toString)

val traitsAB : Traits = new Traits() with TraitAddA with TraitAddB

traitsAB.putMsg("Hello!")

println("traitsAB, value = " + traitsAB.toString)

val traitsBA : Traits = new Traits() with TraitAddB with TraitAddA

traitsBA.putMsg("Hello!")

println("traitsBA, value = " + traitsBA.toString)

val traitsABClass : TraitsABClass = new TraitsABClass

traitsABClass.putMsg("Hello2")

println("traitsABClass, value = " + traitsABClass.toString)
}
}

class TraitsABClass extends Traits with TraitAddA with TraitAddB

class Traits {

var msg : String = ""

def putMsg(m : String) {

msg = m
}

override def toString = {

msg
}
}

trait TraitAddA extends Traits {

abstract override def putMsg(m : String) = {

super.putMsg("A" + m)
}
}

trait TraitAddB extends Traits {

abstract override def putMsg(m : String) = {

super.putMsg("B" + m)
}
}


Program execution results:

traits, value = Hello!
traitsA, value = AHello!
traitsAB, value = ABHello!
traitsBA, value = BAHello!
traitsABClass, value = ABHello2


.

2 comments:

Dave Briccetti said...

Thanks for the info, but it hurts my eyes to try to read that white code on white background, and I can’t find my plugin that turns off your CSS. More contrast, please?

LouisB said...

That's a good point, I'll see if I can adjust the styles to make things more readable and easier on the eyes!