ScalaKnol: Understanding Traits as Stackable Modifications

Traits can be used to modify the methods of a class. The way such modifications are carried out is such that they can stack modifications with each other.

Let us take an example. Say, there is a class called ImageProcessor which processes images. Now there are a few modifications required before the image is actually uploaded to your server. If you are using Facebook then for every image uploaded, it does a few things on the image which might include i) compressing the image ii) scaling that in various sizes iii) extracting a snapshot of the image of the video etc. It might do a ton of other things but let us keep it simple for now.

So lets see some code

  abstract class ImageProcessor { def process(name: String) = println("Base image processing") }
  class ImageProcessorImpl extends ImageProcessor { override def process(name: String) = super.process("myImage") }

So, ImageProcessor is the abstract class and ImageProcessorImpl is the concrete class which provides implementation. Calling a process on the implementation gives us the following result

new ImageProcessorImpl process("image1")          //> Base image processing

Now, what if we want to add modifications to the base image processing, which would say cut a frame out of that image. We define a trait called FrameCutter which does the following

 trait FrameCutter extends ImageProcessor {
    abstract override def process(name: String) = {
      println("Cutting frame"); super.process(name)

We are doing an abstract override here. Such a combination is only possible in traits. What we are doing here is that we are telling the compiler that we are defining a trait which would always be mixed with another trait or class which would provide the concrete implementation of process.

Let us define another trait which compresses the image which is uploaded to the server

  trait ImageCompressor extends ImageProcessor {
    abstract override def process(name: String) = {
      println("Compressing Image"); super.process(name)

Now, these 2 traits describe modifications. They modify the bahavior of the process method and they can be stacked to give stackable modifications. So, let us say we need an object that first “cuts a frame” and then “compresses the image” and then “does the base processing”. Such an object would be defined like

(new ImageProcessorImpl with ImageCompressor with FrameCutter).process("image1")
                                                  //> Cutting frame
                                                  //| Compressing Image
                                                  //| Base image processing

So, we mixed the traits with a concrete class which provides the definition of process. The interesting thing to note here is that with traits, super is dynamically bound on the basis of how the trait is mixed. When a method is called on a trait then the method is called on the trait which is further right in the mix. If this method calls super then it invokes this method on the trait on the left and so,on. In the above example, FrameCutter was the last trait (furthest to the right) which was mixed in. It is called and then the ImageCompressor and then the Concrete class.

Super for classes is always determined statically. Hence for the following code,

  abstract class ImageProcessor { def process(name: String) = println("Base image processing") }
  class ImageProcessorImpl extends ImageProcessor { override def process(name: String) = super.process("myImage") }

super for ImageProcessorImpl is always ImageProcessor.

Ok, coming back to our stackable modifications. What would you do if we need to compress the image first and then cut it? We would mix it the other way i.e.

(new ImageProcessorImpl with FrameCutter with ImageCompressor).process("image1")
                                                  //> Compressing Image
                                                  //| Cutting frame
                                                  //| Base image processing

So we get modifications in traits and we can stack them the way we want to get the desired results. The key things to remember are
i) Behavior of super in traits
ii) Declaring methods with abstract override in traits
iii) Keeping in mind the order of mixin.

About Vikas Hazrati

Vikas is the Founding Partner @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. Knoldus does niche Reactive and Big Data product development on Scala, Spark and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). To know more, send a mail to or visit
This entry was posted in Scala and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s