Scala: Traits & Actors

Traits are Scala’s way of implementing mixins, which means to combine methods of other classes. It’s a powerful tool and an extension to the Java object model. You can think of Traits as Interfaces with concrete implementations.

The Actor model is a robust concurrency abstraction model that was introduced 1973 by Carl Hewitt, Peter Bishop, and Richard Steiger. A number of programming languages since then have adopted this model, probably most notably is the programming language Erlang. Since Erlang is widely used by the Telecommunications Industry or Facebook Chat the Actor model has already become the foundation of some of our day-to-day communication.

In this post I would like to give a brief overview of this two concepts applied in Scala by also providing some examples.

Traits

In Java it is possible to implement as many interfaces as needed to specify all the diverse characteristics of a class. This does not violate the Diamond Problem and makes it possible for developers to apply the useful patterns as the strategy or state pattern. This is in general a good thing, but has one major drawback.

Usually most of the functionality of an interface is valid for all the classes that use it, so that the code is the same and has to be repeated (often even copied) to each class. There is no mechanism for re-usable code. Also quite often an interface implies unrelated members to the rest of the instance. Imagine for example an interface that is used by three classes with each only needing one method of the interface.

Traits can be seen as abstract classes or interfaces with implemented methods. But there is more to them. They define specific and reusable parts of an instance that can be independently maintained. Yes, parts of instances! So they can be applied at instance- as well as class-level. You can for example do the following:

class Person(val name: String){
  def speak = { println("Hello, my name is "+ name) }
}

val tom = new Person("Tom")
val john = new Person("John") with SpecialTrait

tom and john are both instances of the same class Person. But here john is special so we apply a special Trait to him.

Just like interfaces or abstract classes in Java we can also use Traits at class level. A class in Scala can be extended by as many Traits needed using the with statement. Except if it’s the first Trait or abstract class, it needs to use the extend keyword like in Java. So Traits can be used like any other abstract class or interface. And of course Traits can be extended by other Traits.

Stack of Traits

A great refinement in the sense of re-usability is that Traits can be stacked. In the next example we’ll take a closer look at what that means. Stacking Traits basically means that a Trait overrides the function, so that it’s method gets executed first and then the function of the derived instance or trait can be executed. If an instance extends more then one Trait the later Trait overrides the function of the prior and than calls it’s function. The same method is than execute one by one of each Trait that’s implements it and calls the next/stacked implementation. This also means that once a Trait doesn’t call the next function the execution halts, which can be used to apply dependent execution. Let’s see how this works:

trait Language {
  def speak()
}

trait Germane extends Language {
  abstract override def speak() = {
    println("Ich spreche Deutsch.")
  }
}

trait English extends Language {
  abstract override def speak() = {
    println("I speak english.")
  }
}

trait BilingualEnglish extends Language {
  abstract override def speak() = {
    print("I speak english and ");
    super.speak();
  }
}

class Speaker extends Language {
  def speak() = {
    println("???")
  }
}

val speaker = new Speaker
speaker.speak()

val germaneSpeaker = new Speaker with Germane
germaneSpeaker.speak()

val englishSpeaker = new Speaker with English
englishSpeaker.speak()

val bilingualEnglishSpeaker = new Speaker with Germane with BilingualEnglish
bilingualEnglishSpeaker.speak()

In this example we create speakers that speak different languages. As you may have noticed though a English or Germane speaker only speaks English or Germane. This is like a dependent execution of speak, even so we here don’t declare specifically any dependency.

The BilingualEnglish Trait is an example of stacking Traits. Here super points to the next Trait or Instance in the stack. But what is super in this case? At this point it can only be Language, which declares but does not define the speak method. Super is not bound, at least not yet. It will be bound when it is mixed into an instance. This is also the reason why we have to define the function as being abstract. If we would not define it abstract the compiler will complain.

But in what order are stacked Traits and instances executed? They are executed from right to left. So new Speaker with BilingualEnglish with Germane  would still only speak Germane.

You have seen in this post that Traits are a very compelling and powerful extension to the object model. They encapsulate cross-cutting concerns while easing the maintenance of such aspects.

The Actor Model

The Actor Model has been around for quite some years and made it’s appearance in few programming languages. Lately the Actor Model gasp a lot of traction with frameworks like Akka and Microsoft releasing ActorFX, a cloud language for actors. I especial liked this video of the Channel 9 team interviewing Hewitt: The Actor Model (everything you wanted to know, but were afraid to ask). It’s a fine peace of geek talk.

To explain what the Actor Model is, we have to understand that doing concurrency is hard. You have to deal with mutexes, race conditions, synchronization, locks, and a lot more unpleasant concerns. The Actor Model is an lightweight abstraction layer that basically hides this concerns from the programmer. In this model an Actor is the smallest unit of computation. It can be thought of as an object that receives messages and takes action on those messages. One of this action could also be sending a new message to another Actor. The state of an Actor is hidden from the outside and can not be accessed. This already helps to void most cornerstones that arise with concurrency.

One of the most important constraints of an Actor is, that it does not expose any states to the outside world. This means there is no need for synchronization or locking. Let’s have a look how we could implement an Actor based concurrent crawler in Scala:

import scala.actors.Actor
import scala.actors.Actor._

class DeCrawler extends Actor {
  def act {
    loop{
      receive {
        case url:String => 
          println("DE: "+ url)
          Thread.sleep(50)

      }
    }
  }
}

class ComCrawler extends Actor {
  def act {
    loop{
      receive {
        case url:String => 
          println("COM: "+ url)
          Thread.sleep(2000)
      }
    }
  }
}

class CrawlRobot(deCrawler:DeCrawler, comCrawler:ComCrawler) extends Actor {
  deCrawler.start
  comCrawler.start 
  def act = { 
    loop {
      receive {
        case url:String if url.contains(".de") => deCrawler ! url
        case url:String if url.contains(".com") => comCrawler ! url
        case _ => println("Some thing happens")
      }
    }
  }
}

val robot = new CrawlRobot(new DeCrawler, new ComCrawler)
robot.start

val urls = List("google.de", "google.com", "cnn.com", "nytimes.com", "spiegel.de", "tagesschau.de")
urls.foreach( url => robot ! url)

We here have implemented the crawler as an actor that takes an URL as the message and distributes it based on some pattern matching to two crawlers it started upon creation.

Akka Default with Scala 2.11

With Scala 2.11.0, the Scala Actors library is deprecated. Already in Scala 2.10.0 the default actor library is Akka. The above example would have to be migrated. To ease migration the Scala team is providing the Actor Migration Kit (AMK).

Further reading

One thought on “Scala: Traits & Actors

Leave a comment