Scala developers might reach a point when they think the language is not letting them make the code more DRY(Don’t Repeat Yourself) and they have to drop their domain language and start using Factories and Builders or other ambiguous concepts. They end up copy-pasting some trivial boilerplate code around, or in some cases, go to the dark side and write macros that would generate the code for them and thus reduce the maintainability of the code. To work around this problem we can use 2 powerful concepts: Implicit Induction and Type Class Pattern.
In Mathematics we can represent a natural number using Peano numbers. This can be done, by first defining a concept of 0 and a successor(n + 1). By using mathematical induction method, the 0 would be the base case and successor would be the induction step. In programming we can express similar concepts using pattern matching:
Type Class Pattern
In recent years in Scala, as well as in other programming languages, there was an emergency of Type Class Pattern, that was initially introduced in Haskell. The basis is really similar to the notion of working only via Interfaces but provides the ability to express ad-hoc polymorphism.
Derivable Ordering for Nested Tuples
Let’s create a Type Class for Ordering and use Induction to derive instances for a nested tuple.
By having instances for basic types, we first create an instance without using Induction so we can work out the basic wiring.
Now if we choose a tuple structure, that would let us check the first element of the tuple and treat the second element of the tuple as something that can be Ordered, we should be able to use induction. If we choose a structure like (a, (b, (c, d))), we can start using induction. Where simple type that we described before like Int or String is the base case and matching tuple is the inductive step(Just like the Induction example above).
Derivable Ordering for Case Classes
When working with Type Classes it’s quite common to write Type Class instances for each of your Case Classes. As mentioned before it can get tedious and boring and you might end up reaching for macros(I am not saying that it’s bad to write macros, just that you pay the cost of maintaining them). Let’s use Shapeless and it’s HList (heterogeneous list / or in other words, list of many types, that has type safety) to resolve this problem. First, we need to understand that any Case Class can be represented as an HList. For example case class Person(name: String) can be represented as HList (String :: HNil). So the first problem we need to solve is how to derive instances for any HList.
Now we need to transform each Case Class to an HList and we can automatically derive Type Class instances by having primitive instances:
Transforming Between CaseClasses (Bonus)
If any Case Class can be represented by an HList it should be trivial to transform between Case Classes.
When working with Scala functions modeling your system boundaries via interfaces(traits) can provide you with an additional benefit of providing code derivability. You can provide good derivability by creating primitive instances for your Type Classes and by doing so, you can make other fellow developers incorporate your library much easier and your domain much clearer. For example, expressing the bridge between domains with Transformable Type Class.
This post was inspired by:
- https://www.youtube.com/watch?v=CstiIq4imWM Aaron Levin
- https://www.youtube.com/watch?v=Nm4OIhjjA2o Aaron Levin
- https://www.youtube.com/watch?v=auaT2Ft65Qo Thurston Sandberg