Scala Tutorial - Trait Mixin And Linearization

By Nadim Bahadoor | Last updated: February 24, 2020 at 13:14 pm

Overview

In this tutorial, we will learn about the mixin concept when using traits. At the same time, we will also put emphasis, and illustrate, the importance of linearization when it comes to using traits. It goes without saying that mixin as opposed to object inheritance favors composition and, as such, can be thought of to provide you with greater flexibility, as opposed to the classical approach of inheriting some features from a given base class. As its name implies, mixin is the process of putting or gluing together a number of traits in order to benefit from the features that are exposed from these particular traits. Linearization, on the other hand, represents the ordering, or sequencing, of the corresponding traits. If none of these make any sense at the moment, that's OK! As always, we'll provide step-by-step guide to the concepts.

 

As a reminder, and before proceeding further, feel free to review the previous materials on traits as they are especially important:

 

Steps

1. Review of inheritance using traits

We begin with a quick review of inheritance using traits, and create a trait ShoppingCart[T] which defines an abstract printItems() method. We then create a class DonutShoppingCart that extends the trait ShoppingCart, and provides the necessary implementation for the printItems() method. In addition, we’ve used a case class to represent a basic Donut type with a name property of type String as our domain, or business, object.


println("Step 1: Review of inheritance using traits")
case class Donut(name: String)

trait ShoppingCart[T] {
 def printItems(items: Seq[T]): Unit
}

class DonutShoppingCart extends ShoppingCart[Donut] {
 override def printItems(items: Seq[Donut]): Unit = {
 items.foreach(println(_))
 }
}

Without any surprises, we create an object, or instance, of DonutShoppingCart using the new keyword, and thereafter invoke its printItems() method.

val donuts = Seq(Donut("PlainDonut"), Donut("VanillaDonut"))
val donutCart = new DonutShoppingCart()
donutCart.printItems(donuts)

You should see the following output when you run your Scala application in IntelliJ:


Step 1: Review of inheritance using traits 
Donut(Plain Donut)
Donut(Vanilla Donut) 

2. Define trait PrettyPrintUpperCase and mixin with DonutShoppingCart

In your day-to-day programming within a real-world context, you are bound to deal with ever changing requirements! For instance, what if the printItems() method should output all items in the Sequence using capital letters?

 

With mixin and composition in mind, you can easily extend the trait ShoppingCart with a new trait PrettyPrintUpperCase[T]. Thereafter, you can use the with keyword to mixin the latter trait with a particular object, or instance, of DonutShoppingCart - that is, new DonutShoppingCart() with PrettyPrintUpperCase[Donut]. On a side note when working with large enterprise code bases, it is typical for various parts of a given platform to be developed in parallel. To this end, and with the above approach, it would have been fairly straightforward for another developer, or team, to work on the trait PrettyPrintUpperCase. In essence, we were able to modify the behavior of the printItems() method without editing the original class DonutShoppingCart.

println("\nStep 2: Define trait PrettyPrintUpperCase and mixin with DonutShoppingCart")
trait PrettyPrintUpperCase[T] extends ShoppingCart[T] {
 override def printItems(items: Seq[T]): Unit = items.foreach(items => print(item.toString.toUpperCase))
}

val donutCart2 = new DonutShoppingCart() with PrettyPrintUpperCase[Donut]
donutCart2.printItems(donuts)

 

You should see the following output when you run your Scala application in IntelliJ:


Step 2: Define trait PrettyPrintUpperCase and mixin with DonutShoppingCart 
DONUT(PLAIN DONUT)
DONUT(VANILLA DONUT) 

3. Linearization when mixin multiple traits

The Scala Specification provides an in-depth explanation with regards to linearization when mixin is used with multiple traits. We’ll put this into context here by creating another trait PrettyPrintLowerCase[T], which also extends the base trait ShoppingCart[T]. Let us observe what happens when we mixin an object, or instance, of DonutShoppingCart with PrettyPrintUpperCase[Donut], and followed with PrettyPrintLowerCase[Donut].

 

println("\nStep 2: Define trait PrettyPrintUpperCase and mixin with DonutShoppingCart")
trait PrettyPrintLowerCase[T] extends ShoppingCart[T] {
override def printItems(items: Seq[T]): Unit = items.foreach(items => print(item.toString.toLowerCase))
}

val donutCart3 = new DonutShoppingCart() with PrettyPrintUpperCase[Donut] with PrettyPrintLowerCase[Donut]
donutCart3.printItems(donuts)

 

You should see the following output when you run your Scala application in IntelliJ:


Step 3: Linearization when mixin multiple traits 
donut(plain donut)
donut(vanilla donut) 

 

 

From the above output, we notice that the items in the donuts Sequence are printed in small letters. Similarly, let us observe what happens when we mixin an object, or instance, of DonutShoppingCart with PrettyPrintLowerCase[Donut], and followed with PrettyPrintUpperCase[Donut].


val donutCart4 = new DonutShoppingCart() with PrettyPrintLowerCase[Donut] with PrettyPrintUpperCase[Donut]
donutCart4.printItems(donuts)

You should see the following output when you run your Scala application in IntelliJ:


DONUT(PLAIN DONUT) 
DONUT(VANILLA DONUT) 

In this case, the items in the donuts Sequence are printed in capital letters. That being so, you should bear in mind that with the linearization of traits, the order, or sequence, of mixin matters!

 

This concludes our tutorial on Trait Mixin And Linearization and I hope you've found it useful!

 

Stay in touch via Facebook and Twitter for upcoming tutorials!

 

Don't forget to like and share this page :)

Summary

In this tutorial, we went over the following:

  • Review of inheritance using traits
  • Define trait PrettyPrintUpperCase and mixin with DonutShoppingCart
  • Linearization when mixin multiple traits

Tip

  • Now that we’ve covered linearization when using mixin, you can most certainly guess that in large enterprise code base with multiple services, or layers, you should pay special attention to the order of mixin. That is especially true if you are in fact using the Cake Pattern, or other equivalent design.

 

Source Code

The source code is available on the allaboutscala GitHub repository.

 

What's Next

In the next tutorial, I will show you how to define and use The Magnet Pattern.

Nadim Bahadoor on FacebookNadim Bahadoor on GithubNadim Bahadoor on LinkedinNadim Bahadoor on Twitter
Nadim Bahadoor
Technology and Finance Consultant with over 14 years of hands-on experience building large scale systems in the Financial (Electronic Trading Platforms), Risk, Insurance and Life Science sectors. I am self-driven and passionate about Finance, Distributed Systems, Functional Programming, Big Data, Semantic Data (Graph) and Machine Learning.
Other allaboutscala.com tutorials you may like: