Scala Traits – Let’s mix it up a bit

Reading Time: 6 minutes

Scala has way too many unique features that have always stunned the developers worldwide. Some of its features like Lazy evaluation, case classes, etc. have totally changed the coding standards for good.
In this blog today, we will be taking you on a small tour to one of these unique and mesmerizing features that Scala has in store for its users i.e Scala Trait Mixin.

 

download (1).png

 

I have been working with Scala for quite some time now, but whenever I get a chance to work with traits, they simply astonish me. Recently, in one of our projects, I got an opportunity to revisit my basics of Trait Mixin.

Before moving to Scala Trait Mixin, let’s have a look at what traits actually are:

Traits in Scala are like partially implemented interfaces. It may contain abstract and non-abstract methods. It may be that all methods are abstract, but it should have at least one abstract method.

Not only are they similar to Java interfaces, but Scala also compiles them into those with corresponding implementation classes holding any methods implemented in the traits.
A Scala trait looks something like this:

trait ScalaTraits{
  def isLearningFun: String = {
    "yes it is"
  }
}

Also, Traits offer a more fine-grained way to reuse code than normal inheritance.
Like inheritance, traits let you add code to a class that is written elsewhere in the program.
Unlike inheritance, however, a class can “mix in” any number of traits. With inheritance, only one superclass is allowed per class.

Scala Trait Mixins


In Scala,
You can extend any number of traits with a class or abstract class which is called Trait Mixin.

 It is possible to extend any number of Scala traits with a class or with an abstract class. We can extend traits, combinations of traits and classes, or combinations of traits and abstract classes.

 

download (1).png

Let’s try to understand it better with the help of an example: Let’s suppose we have the following:

  • An abstract class Operations
    abstract class Operations{
      def func(x: Int): Int = x
    }
  • 2 traits Doubling and Incrementing
    trait Doubling extends Operations {
      abstract override def func(x: Int): Int = {super.func(2 * x)}
    }
    trait Incrementing extends Operations {
      abstract override def func(x: Int): Int = {super.func(x + 1)}
    }

Let’s try to understand mixin with the help of these.

Example 1:

First, we will be mixin the Doubling trait:

//mixin Doubling
class DoublingOperation extends Operations with Doubling 

val doubleTheValue=new DoublingOperation

Now, doubleTheValue.func(5) will return 10 and not 5.

 

Example 2:

Now, let’s try and understand what happens when we mix 2 traits at a time. We already have 1 abstract class Operations and 2 traits Doubling and Incrementing. Now let’s create a class TestOperations and try mixin both the traits.

class TestOperation extends Operations
val operation1 = new TestOperation with Doubling with Incrementing

The result of operation1.func(5) would be 12
val operation2 = new TestOperation with Incrementing with Doubling

The result of operation2.func(5) would be 11

Now, the question that arises here is that why the result in both the cases is different when the operations on both are similar. The reason for the same is that as Scala resolves the multiple inheritance in a linear, right to left way. The rightmost (the last trait to be mixed in) will resolve first, then the one before that one and so on and so on. As a result of this,

  • In operation1 – the Doubling behavior kicks in first followed by Incrementing
  • In operation2 – the Incrementing behavior kicks in first followed by Doubling

 

Stackable modifications

stack_of_blocks.jpg


Stackable modifications can be combined with each other in any way you desire. Given a set of stackable traits that each modify a class in some way, you can pick and choose any of those traits you would like to use when defining new classes.
Scala allows stackable modifications to any methods using classes and/or traits which means you can choose very specific behavior by stacking classes and traits by mixing them together. When you stack new trait by overriding the same method over and over, you will get different or add-on behavior to your method.

As in the previous examples we saw with the set of multiple traits including Incrementing and Doubling. Let’s add one more trait to the list :

trait Filtering extends Operations {
  abstract override def func(x: Int) = {
    if (x>=6) super.func(x)
    else 0
  }
}

  Now given these 3 traits modifications, you can now pick and choose which ones you want for a particular queue. The order of the mixin will decide the final result. In short, When you call a method on a class with mixins, the method in the trait furthest to the right is called first. If that method calls super, it invokes the method in the next trait to its left, and so on. Using the above example, let’s consider 3 scenarios with the result:

val operation3 = new TestOperation with Filtering with Doubling with Incrementing

The result of operation3.func(5) would be 12 //as filtering at last
val operation4 = new TestOperation with Incrementing with Doubling with Filtering

The result of operation5.func(5) would be 0 // begins with filtering

Thus, Traits form a chain in the order they are mixed in.  

Scala Mixins Order

download
To avoid an error from the compiler, we must maintain an order of the mixins. The right mixins order of trait is that any class or abstract class which you want to extend, first extend them. All the traits will be extended after the class or abstract class.

 

Example:

Let’s try extending the trait Doubling with the abstract class Operations:

class TestOperation extends Doubling with Operations

This is will give a compilation error ‘class Operations needs to be a trait to be mixed in’ as we tried extending traits before the abstract class. Let’s try to reverse the order:

class TestOperation extends Operations with Doubling

This compiles fine. Thus, it is very important to maintain the order of the mixin otherwise the compiler might throw an error.

 

To trait, or not to trait?

  download (2).png

Whenever you implement a reusable collection of behavior, the choice between a trait, an abstract class or a concrete class might be tuff. There is no firm rule as such, but here are a few guidelines that one can consider:

  • If the behavior will not be reused, then make it into a concrete class. It is not reusable behavior after all.
  • If it might be reused in multiple, unrelated classes, then make it a trait. Only traits can be mixed into different parts of the class hierarchy.
  • If you want to inherit it in Java code, then use an abstract class. Since traits with code do not have a close Java analog, it is awkward to inherit from a trait in Java. Inheriting from a Scala class, meanwhile, is exactly like inheriting from a Java class.
  • If you plan to distribute it in compiled form, then lean towards using an abstract class, unless the behavior is only used within the code your group distributes. When a trait changes, classes that inherit from it must be recompiled more frequently than if the trait had been made a class.
  • If efficiency is very important, lean towards using class. Most Java runtimes make a virtual method invocation of a class member a faster operation than an interface method invocation. Traits get compiled to interfaces and therefore pay a slight performance overhead. However, you should make this choice only if you know that the class or trait in question will constitute a performance bottleneck.

However, If you still do not know, after considering the above, then start by making it as a trait. You can always change it later, and in general, using a trait keeps more options open.

That’s enough information on traits for now I guess.
To summarize this blog, we tried to understand what traits in Scala are and how they can be mixed in with abstract classes, classes, and even other traits. Along with this, we covered Stackable Modifications and also had a look at when to use a trait over an abstract class and the vice versa.

If you want to explore more about Scala, please feel free to go through our recent blogs:

Hope This Helps. For any queries, feel free to comment.

Stay Tuned for more.  🙂

 

 

Knoldus-blog-footer-image

 

 

Written by 

Anmol Sarna is a software consultant having more than 1.5 years of experience. He likes to explore new technologies and trends in the IT world. His hobbies include playing football, watching Hollywood movies and he also loves to travel and explore new places. Anmol is familiar with programming languages such as Java, Scala, C, C++, HTML and he is currently working on reactive technologies like Scala, Cassandra, Lagom, Akka, Spark and Kafka.