Mixins (traits) in Scala

Robert Eccardt

I gave a brief introduction to a few features of the Scala programming language at the New York City Java Meetup on October 20, 2008. Notes to that talk are here. I followed up with a second part to my presentation at the December 15, 2008 meeting. I started by noting that the functional side of Scala, which is quite full-featured and robust, can seem very foreign to a person who is used to object-oriented development. And much of the documentation and discussion of Scala I've found around the web assumes the reader is already well-versed in the concepts of functional programming. I recommended Programming in Scala by Martin Odersky, Lex Spoon, and Bill Venners. This book is very thorough and makes no assumptions about prior knowledge of functional programming. I mentioned that the paper version is finally available, though the pre-publication e-book version has been around for a while. Either version can be purchased at the Artima website.

Once again, I wanted to focus on something that a Java programmer would find easy to grasp, and might consider an improvement over Java. This time, though, I focused on a single feature, mixins.

Like Java, Scala allows a class to extend only only a single other class. The original intention in Java was to avoid the complications encountered in languages with multiple inheritance. Instead, Java allows the specification of abstract requirements using interfaces. Java classes are allowed to implement multiple interfaces, and through the use of inner classes and/or anonymous implementation of classes, many of the difficulties of multiple inheritance can be avoided. However, there is the drawback that since interfaces are completely abstract, all implementers are required to provide all functionality of the interface.

Scala achieves the best of both worlds through mixins, or traits. They give the feel of allowing partial implementation of interfaces, allowing later classes or traits to implement more or all of the remaining abstract behavior.

As I mentioned in the October talk, Scala is quite interoperable with already existing Java code. Here's an example of how one could implement a very familiar Java interface, Comparator:

    import java.util.Comparator

    class StringComparator extends Comparator[String] {
        override def compare(left: String, right: String):Int =  { 
          return left.compare(right); 
        }
      }
    
    val x = new Comparator[String] {
        override def compare(left: String, right: String) =  left compare right
      }

The Scala class StringComparator implements Comparator on type String using the verbose Java style with all the semicolons and curly brackets. Note, though, the use of "extends" rather than "implements" - Scala has neither the "implements" nor the "interface" keyword. The val x is assigned an anonymous implementation of the same thing using the terse Scala style, eliminating unneeded curlies, semis, and dots.

If we wanted to do a partial implementation of an interface, we could extend the interface with a trait rather than a class:

Java interface:
public interface TestInterface {
	void methodOne();
	void methodTwo();
}

Scala implementation:
trait TraitOne extends TestInterface {
  override def methodOne = println("methodOne")
  }

trait TraitTwo extends TestInterface {
  override def methodTwo = println("methodTwo")
  }

object TraitTester {
 def testEm = new TraitOne with TraitTwo methodTwo  
}

TraitTester's testEm method news a TraitOne, mixes in a TraitTwo, and calls methodTwo. Even though TraitOne and TraitTwo are both abstract on their own, between the two of them they implement all the methods of TestInterface. The combination of the two (using the "with" keyword) produces a concrete class, and the object generated is of this unnamed type.

This was the compilable listing I showed in Eclipse to illustrate some of these features:

package test

trait HelloSayer {
  def sayHello = println("hello")
  }

trait GoodbyeSayer {
  def sayGoodbye = println("goodbye")
  }

class MyClass extends HelloSayer with GoodbyeSayer {
  sayHello
  sayGoodbye
}

class MyClass2

object Mixin1 {
  def main(args : Array[String]) : Unit = {
    import java.util.Comparator

    class StringComparator extends Comparator[String] {
        override def compare(left: String, right: String):Int =  { 
          return left.compare(right); 
        }
      }
    
    val x = new Comparator[String] {
        override def compare(left: String, right: String) =  left compare right
      }

    new MyClass
    val mc2 = new MyClass2 with HelloSayer with GoodbyeSayer
    mc2 sayGoodbye
  }
}

The output is

hello
goodbye
goodbye

While Scala is a statically compiled language, with all the implied performance and type checking benefits, I get the feeling of a dynamic language because of this ability to compose a new, unnamed class by mixing in traits (using the "with" keyword) when newing an object.

Another feature of traits I particularly like is their stackablity. Traits can have abstract override methods of the class they extend that call the immediate super version of that method. But their exact location in the inheritance chain is not known until we actually compose the class to be used. Thus at design time of the trait, the exact super version is not known. Here is a concrete example:

package test

class PassThrough {
  def processInt(i: Int) = {
    println("got " + i + " will just pass it through")
    i
    }
}

trait Doubler extends PassThrough {
  abstract override def processInt(i: Int) = {
    println("Doubler got " + i + " calling super")
    val j = super.processInt(i)
    println("Doubler got " + j + " from super; will double it")
    j * 2
  }
}

trait Incrementer extends PassThrough {
  abstract override def processInt(i: Int) = {
    println("Incrementer got " + i + " calling super")
    val j = super.processInt(i)
    println("Incrementer got " + j + " from super; will add one")
    j + 1
  }
}

object Mixin2 {
  def main(args : Array[String]) : Unit = {
    val pt = new PassThrough with Doubler with Incrementer 
    println(pt processInt 2)
  }
}

The object we assign to the val pt in main mixes in to PassThrough first Doubler, then Incrementer. This means that Incrementer's processInt's call to super is a call to Doubler's processInt. Doubler's call is to PassThrough's processInt. The output makes this chain clear:

Incrementer got 2 calling super
Doubler got 2 calling super
got 2 will just pass it through
Doubler got 2 from super; will double it
Incrementer got 4 from super; will add one
5

But by changing this line:

val pt = new PassThrough with Doubler with Incrementer

to this:

val pt = new PassThrough with Incrementer with Doubler

we are changing the order of the mixing in and thus the chain of super method calls. We get the following output:

Doubler got 2 calling super
Incrementer got 2 calling super
got 2 will just pass it through
Incrementer got 2 from super; will add one
Doubler got 3 from super; will double it

In his The Seductions of Scala, Part I post, Dean Wampler presents a very elegant implementation of the observer pattern using Scala traits. I made some substantial modifications to the code and showed it at my talk. With all its reliance on mutable state, this code is not an example of functional programming, but I think it's a nice demonstration of Scala's potential as a "better Java" on the object oriented side.

package test

class Observer[S](val receiveUpdate: S => Unit)

trait Subject[S] { 
    this: S =>
    private var observers: List[Observer[S]] = Nil
    def addObserver(observer: Observer[S]) = observers = observer :: observers
    def notifyObservers = observers foreach(_.receiveUpdate(this))
}

class Account(initialBalance: Double) {
    private var currentBalance = initialBalance
    def balance = currentBalance
    def deposit(amount: Double)  = currentBalance += amount
    def withdraw(amount: Double) = currentBalance -= amount
}

class ObservedAccount(initialBalance: Double) extends Account(initialBalance) 
                                              with Subject[Account] {
    override def deposit(amount: Double) = {
        super.deposit(amount) 
        notifyObservers
    }
    override def withdraw(amount: Double) = {
        super.withdraw(amount)
        notifyObservers
    }
}

object Mixin3 {
  def main(args : Array[String]) : Unit = {
    val account = new ObservedAccount(100.0)
    account.addObserver(new Observer((a: Account)=> 
                                   println("Observed balance: " + a.balance)))
    println("Starting balance: " + account.balance)
    println("Depositing $10.00")
    account.deposit(10.0)
    println("Withdrawing $5.60")
    account.withdraw(5.6)
  }	
}

Starting with the Account class, we see that is quite straight-forward, with a private member that contains the current balance, and methods for depositing and withdrawing. Obviously not real-world, since there are no provisions for concurrency - it's only a demo, after all!

Next, we see that the ObservedAccount is derived from Account. It overrides the deposit and withdraw methods, which both just call their super versions and then call notifyObservers. Where did notifyObservers come from? It's a memeber method of Subject, which we mixed in, parameterized on Account (recall that Scala generics are formed with square brackets rather than angle brackets).

Moving on to the trait Subject, we start with a little magic in the first line of the body:

this: S =>

This is called the "self type". It essentially tells the compiler that the type after the colon will be mixed in. Here we make it the type the trait Subject is parameterized on. Thus, a Subject[Account] will satisfy a requirement for an Account, which we will take advantage of later.

The rest of Subject uses syntax that was explained in my October talk. But in English, observers is a list of Observers, addObserver just adds one to the list, and notifyObservers iterates through all the Observers in the list, calling each one's receiveUpdate method with "this" passed as its argument. Because of the self type, "this" will qualify as an Account, which is what receiveUpdate will require when the parameters are finally resolved.

The Observer class is initialized with a function that takes an argument of the generic type of the Observer, and returns nothing (Unit). The function is available to callers as receiveUpdate.

Finally, in main we create an ObservedAccount with an initial balance of 100. We add an Observer which is given a function that just prints out the balance. Then we put it through its paces. Here is the output:

Starting balance: 100.0
Depositing $10.00
Observed balance: 110.0
Withdrawing $5.60
Observed balance: 104.4