Scala traits

Scala extends the idea of interfaces introducing traits. A trait is something in between an interface, because it states the nature of methods and even fields, and a full superclass, since a trait can also implement methods and fields.

Traits are Scala answer to the multiple inheritance problem. Other languages like C++ allow a class to inherit from multiple classes. When two or more superclasses declare the same method, the subclass is forced to reimplement that method to clarify what it does. Java on the other hand excludes multiple inheritance to avoid this kind of complexities. A Java class can only extend one single superclass. Scala keeps this restriction (a class extends only one superclass) but mitigate the drawbacks by allowing the subclass to mix in as many traits it needs.

This is a declaration of a trait in Scala:

trait Resilient {
  def replicaFactor: Int
  def checkReplica: Boolean
}

The Resilient trait adds some features (this it the meaning of the term trait, like in “somatic trait”) to any class that mixes it in. This use of traits is very similar to writing a standard Java interface: it declares two methods, the first returns the replica factor, the other is supposed to check some replica strategy. Let’s see how this trait can be used with a generic Node class and its DataNode subclass:

class Node {
  def name: String = ""
  def start: Unit = {}
  def stop: Unit = {}
}

Each Node object will have a name and will provide a start and stop method. The DataNode extends this base class using the Resilient trait:

class DataNode extends Node with Resilient {
  def replicaFactor = 2
  def checkReplica = {
    /* do some check here */
    true
  }
}

The syntax may seem odd at first. Why Scala designers have not reused the extends keyword for each trait? Why using with? Well, the reason is Scala does not think the class is extending several things (Node, then Resilient), one after the other. Scala thinks the subclass is extending just one thing, formed by the composition of one superclass and a set of traits (Node with Resilient). This is called a compound type.

Instantiating a DataNode class like:

val dNode = new DataNode()

creates an object with the Node legacy mixed with the Resilient legacy. A trait can be even mixed in during class instantiation, like in:

val dNode = new Node() with Resilient {
  def replicaFactor = 2
  def checkReplica = {
    /* do something here */
    true
  }
}

Here the trait is mixed in and implemented in the same step, providing on the fly the required methods.

A trait can also extend another trait. The Resilient trait, as an example, could be specialized into a DiskResilient trait and a MemoryResilient trait:

trait DiskResilient extends Resilient {
  override val replicaFactor = 3
  override def checkReplica: Boolean = { /* concrete implementation here */ }
}

trait MemoryResilient extends Resilient {
  override val replicaFactor = 2
  override def checkReplica: Boolean = {/* concrete implementation here */ }
}

Here we see two important features of Scala at work. The first is the aforementioned possibility to extend a trait from another trait, using the same extends keyword you use for classes. The other, more important, is the trait ability to implement concrete methods, something just impossible with Java interfaces. This is major improvement, since in Java concrete implementations are usually provided by interface companion abstract classes. However, as we said, a Java class can only extend one superclass at time, so it’s impossible to extend two companion classes at the same time, when implementing their respective interfaces. A developer is forced to implement all the required methods starting from the second interface the class implements. Scala however allows a class to mix in as many traits its developer desires, so as long as traits provide concrete method implementations, a class can really be built of mixed in methods only.

To make things even more complex (but flexible), a trait can also extend a real class! That’s really surprising. How’s possible for a trait to extend a class and then be mixed into another class that already extends a superclass, without violating the single inheritance principle? Let’s slow down and look at the details in slow motion. If we redeclare the Resilient trait as extending Node, things are still quite easy:

trait Resilient extends Node {
  def replicaFactor: Int
  def checkReplica: Boolean
}

Since DataNode is declared extending Node with Resilient, Scala raises no flags, because both extend the same superclass. By the way, this is the way the DataNode class is constructed:

  1. Node superclass constructor is executed
  2. Resilient trait is visited. Since it has a superclass, it is visited first to execute its constructor
  3. However Resilient superclass is the Node class, whose constructor has already been executed once.
  4. Resilient constructor can then be executed
  5. Lastly DataNode constructor is executed

Now, what happens if Resilient would instead extend another class?

class RedundantSystem { ... }

trait Resilient extends RedundantSystem { ... }

class DataNode extends Node with Resilient { ... }

Well, Scala simply refuses to compile this code.

traits.scala:15: error: illegal inheritance; superclass Node
 is not a subclass of the superclass RedundantSystem
 of the mixin trait Resilient
class DataNode extends Node with Resilient {

The developer has to solve this inheritance problem before compiling its code. Pay attention to an aspect: the message says that Node is not a subclass of RedundantSystem. This means that subclasses and mixed in traits are not forced to share the same superclass, but are forced to share a common ancestor.

Traits have other ways to state that a class must fulfil some requirements to be mixed in. This is called the trait self type and is expressed by this syntax:

trait T {
  this: SuperClass =>
    def method1 { ... }
    def method2: Int = { ... }
}

Any class extending SuperClass can mix in the trait T. Other classes are prevented from mixing in this trait. There’s a major difference in using self types in place of extending a superclass: the trait does not inherit the superclass methods and fields!

Self types can even use structural types, Scala very granular way to specify type features. A structural type describes a type by declaring its features, one by one. A trait is hence able to clarify that it can be mixed into any class, no matter what, as long as that the class implements some features. Here is an example:

trait Alarm {
  this: { def ringBell() : Unit } =>
    def alert(message: String) {
      ringBell()
      println("Warning: " + message)
    }
}

class Sentinel extends Alarm {
  def ringBell() { /* so something to ring the bell */ }
  /* more methods here */
}

class Watchdog extends Alarm {
  def ringBell() { /* do something to ring another bell */ }
  /* more barking methods here */
}

Here the Sentinel class and the Watchdog class can mix in the Alarm trait since both define a ringBell() method. This is much more flexible than restricting to a named class, since any class implementing the required methods is allowed to mix in the trait, no matter which superclass it extends.

Traits are one of the many stunning features Scala provides to write elegant and resilient code. Comparing them to classes can be useful when starting, to get acquainted to their use. You can even declare a function as returning a trait, instead of a class:

def f(): Resilient = { ... }

The only thing traits do not support are constructor parameters. This is something I’ll address in a future post.