In our previous blog The Story of Trait – Part 3, we have discussed the mixin, one of the charming feature of traits. Now we are going to explore chaining of traits.
We have seen how can we mix more than one traits into classes. But what would happen if two traits have same method i.e same name and same signature?
This is where Multiple Inheritance fails. And Traits works really well in such scenario because Traits are Stackable.
What is meant by Stackability?
Suppose we have traits T1, T2 and class A. We have another class B which extends class A and mix traits T1 and T2.
What will happen in this case?
Methods in T1 and T2 instead of colliding with each other they collaborate to form a chain.
An instance of B will chain with an instance of T1 which will chain to an instance of T2. So, when a method is invoked which has the same name in both traits, the first T2 will handle it and T2 could forward it to T1 which after doing some work could forward to an instance of B. And that removes the collision and ends up collaborating nicely.
Let’s take an example from Programming in Scala Book,
We have an abstract class Queue and implemented by a class QueueImplementation.
Well, that works fine. What we need to put the double elements in the queue. We don’t want to change the implementation of Queue. Because Future might demand different implementation like incrementing element. So, We could define traits to provide different modifications like incrementing and doubling the element of Queue. Let’s try creating a trait for double elements.
Here extends does not mean inheriting from Queue. It is used as a constraint here. extends means that trait can only be used or mixin those classes which extend from Queue like QueueImplementation. For more info refer.
Likewise, super here does not mean going to base class in the context of the inheritance hierarchy. But it means going from right to left in chaining of traits. It performs the late binding of that call. And the method is declared abstract override, this is a deadly combination of abstract and override together. By using the keyword override means, we are telling Scala that we are providing an implementation of a known method from the base class. At the same time, we are saying that the actual final implementation of this method will be provided by a class that mixes in the trait.
Let’s define another trait which will increment the element of Queue.
We have created an object of QueueImplementation with mixing Incrementing and Doubling trait to it. What would be the output if we enqueue any element i.e 4. Will it get doubled or incremented? Will it be 8 or 5?
We know traits are stackable, So when we enqueue the element 4, what it did first it went to Doubling, which doubled the elements and then went to Incrementing which increment doubled element by 1 and then went to QueueImplementation which enqueue the element in the queue.
Similarly, this time first call went to Incrementing which incremented the value and then went to Doubling to double the value and finally to QueueImplementation class to put the element in the queue.
We have seen how Scala Traits are Stackable. There is no diamond problem in Scala because methods don’t collide instead they start collaborating with each other to form a chaining. The method call is determined by linearization of the classes and traits that are mixed into a class. When we instantiate a class with new, Scala takes the class, and all of its inherited classes and traits, and put them in a linear order. And super call starts calling a method from right to left and net result is the stackable behaviour.
Please feel free to suggest and comment.