In this tutorial, you will learn about the very important concept of Algebraic Data Types (ADT) in Scala.
Let’s begin with what we already know, enum or enumeration types.
1. Enumeration Types
An enumeration is used to define a data type that consists of a set of named values. All the value in an enumeration share a unique type known as Value type member of the enumeration.
Creating Enums in Scala
To create a enum type in Scala, we need to create an object that extends the Enumeration type. This is because Scala does not provide an enum keyword.
The code below creates an enum type names Size with three values: Small, Medium and Large
object Sizes extends Enumeration { type Sizes = Value val Small, Medium, Large = Value }
You can print the string value of enumeration using the code below:
println(s"My size is ${Size.Medium}")
Pattern Matching on Enum Values
Although we’ll talk about pattern matching in a different tutorial, the code below shows how to perform some operation base on pattern matching on values of an enum.
Size.values.foreach { case s if (s == Size.Small ) => { println(s) } case s if (s == Size.Medium) => { println(s) } case s if (s == Size.Large) => { println(s) } }
2. Algebraic Data Types
In theory, algebra can be viewed as:
- a set of objects
- as set of operations that can be applied to those objects to create new objects
For example, we can apply a set of arithmetic operations (+, -, * and /) to a set of numbers to produce new numbers.
In Scala, we can apply certain operation to a set of types to produce new type(s).
Let’s create a new type called Weather from a set of objects: Windy, Cloudy, Rainy and Overcast
sealed trait Weather case object Rainy extends Weather case object Windy extends Weather case object Cloudy extends Weather case object Overcast extends Weather
The sealed trait means that only these four values defined here can extend the Weather type. Let’s now talk about a specific algebraic data type: the sum type
3. The Sum Type (Union Type)
The Sum type helps us define a type that can have certain values. With the sum type, we enumerate all the possible instances of a type. The following are important to know about the sum type:
- they are create with a sealed trait as the base type
- the individual instances are created as case objects
- the “is-a” relationship is used for describing the sum type
- instances of the sum type can be parameterized
In the code below, we define an Employee type which could be either a Doctor, Driver or Intern. This is represented below:
- Doctor has two parameter: name: String and specialty: String
- Driver has two parameter: name: String and assignedCar: String
- Intern has one parameter: age
The equivalent Scala code is given below:
sealed trait Employee case class Doctor(name:String, specialty: String) extends Employee case class Driver(name:String, assignedCar: String) extends Employee case class Intern(name:String, age: Int)extends Employee
Now you that you understand ADTs, you can create a couple more just to get used to it.
4. Pattern Matching With ADTs
Pattern matching allows us to decompose a specified ADT using the extractor method. With it, we can extract the fields of an ADT. This means that the case classed would have to implement unapply method to be an extractor.
Let’s define a pattern match for our Employee ADT
def isEmployee(e: Employee) : Boolean = e match { case Doctor(name, specialty) => true case Driver(name, assignedCar) =>true case Intern(name, age) => false }
In the case of pattern matching code above, we define a function that takes an Employee object and returns a boolean depending on the particular instance.