The Artima Developer Community
Sponsored Link

Scalazine
Scala's Selfless Trait Pattern
by Bill Venners
September 4, 2009
Summary
This article describes a simple Scala design pattern that allows library designers to provide services that their clients can access either through mixins or imports. Giving users a choice between mixin composition and importing makes a library easier to use.

When designing a Scala library, you can partition the services offered by the library into traits. This gives your users more flexibility: they can mix into each class only the services they need from your library.

For example, in ScalaTest you create a basic suite of tests by mixing in any of several core Suite traits:

class MySuite extends Suite // a basic suite of test methods
class MySuite extends FunSuite // a basic suite of test functions
class MySpec extends Spec // a BDD-style suite of tests
class MyeSpec extends FeatureSpec // a higher level BDD-style suite 
class MySuite extends FixtureSuite // a suite of tests that take a fixture parameter

You can also mix in any of several other traits that add additional, or modify existing, behavior of the core trait:

class MySuite extends FunSuite with ShouldMatchers with EasyMockSugar with PrivateMethodTester

Although splitting a library's behavior into composable parts with traits gives users flexibility, one downside is that users may end up repeatedly mixing together the same traits, resulting in code duplication. User can easily eliminate this duplication, however, by creating a convenience trait that mixes together the behavior they prefer, and then mixing in that convenience trait into their classes instead. For example:

trait ProjectSpec extends WordSpec with ShouldMatchers with EasyMockSugar
class OneSpec extends ProjectSpec
class TwoSpec extends ProjectSpec
class RedSpec extends ProjectSpec with PrivateMethodTester
class BlueSpec extends ProjectSpec

Besides just mixing together the things they need, users can add value inside their convenience traits. For example, projects often require many tests that share a common fixture, such as a connection to a database containing a clean set of test data. This could be addressed simply by creating yet another trait that mixes in ProjectSpec and adds the needed pre- and post-test behavior:

trait DBProjectSpec extends ProjectSpec with BeforeAndAfterEach {
  override def beforeEach() {
    // initialize the database and open the connection
  }
  override def afterEach() {
    // clean up the database and close the connection
  }
}

Now test classes that need a database can simply mix in DBProjectSpec:

class MySpec extends DBProjectSpec

Although the ease of behavior composition afforded by traits is very useful, it has some downsides. One downside is that name conflicts are difficult to resolve. A user can't, for example, mix together two traits that contain methods with signatures that cause an overload conflict. Still another downside is that it is slightly awkward to experiment with the services offered by a trait in the Scala interpreter, because before the trait's services can be accessed, it must be mixed into some class or object. Both of these downsides can be addressed by making it easy to import the members of a trait as an alternative to mixing them in.

Implementing the selfless trait pattern

Scala has two features that make it easy to offer users this choice between mixins and imports. First, Scala allows users to import the members of any object. Second, Scala allows traits to have a companion object---a singleton object that has the same name as its companion trait. You implement the selfless trait pattern simply by providing a companion object for a trait that itself mixes in the trait. Here's a simple example of the selfless trait pattern:

trait Friendly {
  def greet() { println("hi there") }
}

object Friendly extends Friendly

Trait Friendly in this example has one method, greet. It also has a companion object, named Friendly, which mixes in trait Friendly. Given this friendly design, client programmers of this library can access the services of Friendly either via mix in composition, like this (imports and uses of Friendly are in bold):

object MixinExample extends Application with Friendly {
  greet()
}

Or by importing the members of the Friendly companion object, like this:

import Friendly._

object ImportExample extends Application {
  greet()
}

Although the external behavior of MixinExample is the same as ImportExample, when MixinExample invokes greet it is calling greet on itself (i.e., on this), but when ImportExample invokes greet, it is calling greet on the Friendly singleton object. This is why being able to fall back on an import allows users resolve name conflicts. For example, a user would not be able to mix the following Functional trait into the same class as Friendly:

trait Functional {
  def greet: String = "hi there"
}

Because Friendly and Functional's greet methods have the same signature but different return types, they will not overload if mixed into the same class:

object Problem extends Application with Friendly with Functional // Won't compile

By contrast, the offending method can be renamed on import, like this:

import Friendly.{greet => sayHi}

object Solved extends Application with Functional {
  sayHi()
  println(greet)
}

A good real-world example is trait ShouldMatchers from ScalaTest, which follows the selfless trait pattern. I expect the most common way ShouldMatchers will be used is by mixing it into test classes, often by means of a convenience trait. Here's an example:

import org.scalatest.WordSpec
import scala.collection.mutable.Stack
import org.scalatest.matchers.ShouldMatchers

class StackSpec extends WordSpec with ShouldMatchers {

  "A Stack" should {

    "pop values in last-in-first-out order" in {
      val stack = new Stack[Int]
      stack.push(1)
      stack.push(2)
      stack.pop() should be === 2
      stack.pop() should be === 1
    }

    "throw NoSuchElementException if an empty stack is popped" in {
      val emptyStack = new Stack[String]
      evaluating { emptyStack.pop() } should produce [NoSuchElementException]
    }
  }
}

Occasionally, however, users may want to experiment with the ShouldMatchers syntax in the Scala interpreter, which they can do with an import:

scala> import org.scalatest.matchers.ShouldMatchers._
import org.scalatest.matchers.ShouldMatchers._

scala> Map("hi" -> "there") should (contain key ("hi") and not contain value ("dude"))

scala> List(1, 2, 3) should have length 2
org.scalatest.TestFailedException: List(1, 2, 3) did not have length 2
	at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:148)
	at org.scalatest.matchers.ShouldMatchers$.newTestFailedException(ShouldMatchers.scala:2318)
	at org.scalatest.matchers.Matchers$ResultOfHaveWordForSeq.length(Matchers.scala:2891)
	at .(:7)
	at .()
	...

The reason this is called the selfless trait pattern is that it generally only makes sense to do this with traits that don't declare a self type. (A self type is a more specific type for this that restricts what the trait can be mixed into.) One other way in which traits that follow this pattern are "selfless" is that rather than forcing users to mix in their services they instead give users a choice: either mixing in the trait or importing the members of its companion object. If you are designing a Scala library that has a trait that doesn't declare a self type, consider implementing the Selfless Trait pattern by creating a companion object that mixes in the trait.

Share your opinion

Have a question or opinion about the Selfless Trait pattern? Discuss this article in the Articles Forum topic, Scala's Selfless Trait Pattern.

Resources

The ScalaTest examples shown in this article are taken from ScalaTest 1.0:
http://www.artima.com/scalatest/#snapshot

About the author

Bill Venners is president of Artima, Inc., publisher of Artima Developer (www.artima.com). He is author of the book, Inside the Java Virtual Machine, a programmer-oriented survey of the Java platform's architecture and internals. His popular columns in JavaWorld magazine covered Java internals, object-oriented design, and Jini. Active in the Jini Community since its inception, Bill led the Jini Community's ServiceUI project, whose ServiceUI API became the de facto standard way to associate user interfaces to Jini services. Bill is also the lead developer and designer of ScalaTest, an open source testing tool for Scala and Java developers, and coauthor with Martin Odersky and Lex Spoon of the book, Programming in Scala.


Sponsored Links

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us