Mastering Generic Self-Type Traits and Type Bounds in Scala: A Guide to Best Practices and Advanced Techniques

Saeid Dadkhah
CodeX
Published in
3 min readJan 23, 2023

Self-types are a way to declare that a trait must be mixed into another trait, even though it doesn’t directly extend it.

Scala — Generic Types, Traits, and Upper Type Bounds
Scala — Generic Types, Traits, self types, and Upper Type Bounds

The self-type definition can be utilized to access the superclass. Further information on this topic can be found here. However, it is assumed that the reader of this blog is already familiar with the concept of self-type traits and is specifically interested in the implementation of a generic self-type trait.

Case Study

The presence of unnecessary whitespace within a class can negatively impact code readability and maintainability. Smart constructors or factories can be utilized to address this issue. Specialized cleaning functions for handling the cleaning process are the alternative. This allows for the preservation of both versions for different use cases.

The Mess class

The implementation of a trait that provides a method for cleaning its subclasses can be useful in addressing the issue of unnecessary whitespace within a class. This trait can be defined as follows:

The Cleaner trait

Now, The class Mess can then extend this trait and implement the clean method.

The Mess class implementing the clean method

Now, we have a clean object.

Call only One Method

In order to achieve a completely clean object, it may be beneficial to implement an additional method in the Cleaner trait, which utilizes the clean method in a repeated or recursive manner.

The Cleaner trait defining the cleanAll method

The implementation of the cleanAll method should be provided within the Mess class, which extends the Cleaner trait.

The Mess class implementing the cleanAll method

Generic Self-Type Traits at Its Best

In order to reduce code redundancy, it may be appropriate to include the implementation of the cleanAll method within the Cleaner trait. This ensures that any class that extends the Cleaner trait will have access to the cleanAll method and its implementation, without the need for duplicate code within each individual class.

The Cleaner trait implementing the cleanAll method

But this implementation will result in a compile-time error.

The compile error of Cleaner

The assumption that the type parameter T of the Cleaner trait is a superclass of the implementing class, in this case Mess, may not always be valid. The implementation of the Mess class may specify a different type as T and still conform to the requirements of the Cleaner trait, leading to a valid implementation.

The UnwantedMess class

It is not possible to invoke the clean method on the unwantedMess object as it is not an instance of the Cleaner trait. How can we add the assumption to the implementation of Cleaner?

Upper-type bound acts as a superhero here and will save us! To further enhance encapsulation, the clean method can be designated as protected and the cleanAll method can be marked as final.

The Cleaner trait with type bound

Incompatibility of the UnwantedMess class with the constraints set by the Cleaner trait will result in a compilation error.

The compile error of UnwantedMess

Conversely, the implementation of the Mess class is expected to conform to the constraints set by the Cleaner trait and thus will not result in compilation errors.

The Mess class inheriting the implementation of the cleanAll method

Use Case

I faced this issue when I was trying to implement a PATCH method in a REST server. Can you think of any other use case for this method?

A practical implementation of the generic self-type trait is available in the ‘23_update’ project within my GitHub repository. Feel free to check it and open issues and PRs.

I hope you enjoyed this tutorial. Thank you.

--

--