1. Introduction
In this tutorial, we’re going to explore what Algebraic Data Types (ADT) are and how they can be used to define our data model in Scala.
2. What Are Algebraic Data Types?
ADTs are commonly used in Scala. Simply put, an algebraic data type is any data that uses the Product or Sum pattern.
The question now shifts to understanding what Product and Sum Types are.
3. The Product Type Pattern
When we need to model data containing other data we can use the product type pattern.
Let’s define a simplified model of chess pieces:
case class ChessPiece(color: Color, name: Name)
val rook = ChessPiece(White, Rook)
In this case, “ChessPiece has a Color and a Name”. Later on in the article, we’ll drill down in the Color and Name definition.
In Object-Oriented terminology, this is a “has-a” relationship.
The ChessPiece data contains other data. A product type is often defined as a case class.
4. The Sum Type Pattern
If we need to define a type which can assume different values, we can leverage the sum type pattern.
Going back to our chess piece example we can define Color and Name as follows:
sealed trait Color
final case object White extends Color
final case object Black extends Color
sealed trait Name
final case object Pawn extends Name
final case object Rook extends Name
final case object Knight extends Name
final case object Bishop extends Name
final case object Queen extends Name
final case object King extends Name
Using object-oriented terminology, we can express the Sum Type relationship as “is a”.
In the above example “Color is a White or a Black”. Sealed traits are the way of defining Sum Types.
5. Some More Patterns
We’ve seen in the examples of the previous chapter has-a and is-a relationships combined with and/or operators:
- ChessPiece has a Color and a Name
- Color is a White or a Black
There are still two combinations we are missing: has-a + or and is-a + and.
The former, has-a + or, can be expressed in the following way:**
trait Semaphore {
val color: Color
}
sealed trait Color
final case object Green extends Color
final case object Amber extends Color
final case object Red extends Color
In our code “Semaphore has-a Color, where Color is Green or Amber or Red”.
The last pattern we are gonna look at is is-a + and, which we can translate in code in this way:**
trait Feline
trait Animal
trait Cat extends Animal with Feline
In our example “Cat is an Animal and a Feline”.
6. Pattern Matching
The use of pattern matching comes naturally when using ADTs, encouraging the use of this functional programming idiom.
In the following example, let’s see how to leverage the product and sum type to write a method telling us if a piece is the most important of our board. We can check the attribute name, a sum type*,* of ChessPiece, a product type*,* to determine which piece we are processing:
def isTheMostImportantPiece(c: ChessPiece): Boolean = c match {
case ChessPiece(_, King) => true
case _ => false
}
This is an example of how pattern matching comes extremely in handy in this case and goes along really well with ADT.
7. Conclusions
In this article, we saw how to translate object relationships in Scala code. Being aware of these patterns and recognizing them is important to write idiomatic functional code.
As always, the code is available over on GitHub.