Mike Slinn

Algebraic Data Types

— Draft —

Published 2019-09-22.
Time to read: 2 minutes.

This lecture discusses sum types, which are a kind of algebraic data type (ADT). Using ADTs in your code can make it more readable and more error-free by defining an ADT subtype for every possible state or discrete value of a variable.

The sample code for this lecture can be found in courseNotes/src/main/scala/ADT.scala.

Sum types are a kind of algebraic data type (ADT). In Scala, sum types are formed by defining multiple types that extend a sealed trait. This allows match expressions to be written that are guaranteed to handle all of the subtypes of the sealed trait. I’ll show you why this is useful in a moment. Before we explore that, you need to know that if the definition of a trait is prefixed with the sealed keyword, all subtypes of that trait must be defined in the same file as the trait. Let’s look at an example.

Scala code
sealed trait Index
class LecturesIndex extends Index
class SectionsIndex extends Index
class CoursesIndex  extends Index
class GroupsIndex   extends Index
class SitesIndex    extends Index

Now we can use a match statement that operates on an instance of Index. The Scala compiler will issue a warning if one of the Index subtypes is not handled.

Scala code
def showEm(index: Index): Index = index match {
  case si: SitesIndex =>
    println(si)
    si
case gi: GroupsIndex => println(gi) gi
case ci: CoursesIndex => println(ci) ci
case si: SectionsIndex => println(si) si
case li: LecturesIndex => println(li) li }

Note that each match case in the above code block returns the Index subtype instance. This allows the showEm method to be chained with other methods provided by the Index subtypes. Here are some example of how the showEm method could be invoked.

Scala code
showEm(new LecturesIndex)
showEm(new SectionsIndex)

You can run this code by typing:

Shell
$ sbt "runMain ADT1"

Return Values From Base Trait Methods

To define a method that is common to all of the types in an ADT, define the method in the base trait. For example, I’ve modified the Index base trait to define a method called show that prints out the value of the type (or subtype) and returns the valu.

Scala code
sealed trait Index {
  def show(): Index = {
    println(this)
    this
  }
}

Lets also enhance the LecturesIndex and SectionsIndex classes to define methods lectureMethod1 and sectionMethod1, respectively:

Scala code
class LecturesIndex extends Index {
  def lectureMethod1: LecturesIndex = {
    println("LectureMethod1")
    this
  }
}
class SectionsIndex extends Index { def sectionMethod1: SectionsIndex = { println("SectionMethod1") this } }

Note that even though every method return the subtype that defines it, for example SitesIndex or GroupsIndex, the Index trait declares the return type of the show method to be Index, which limits the usefulness of the returned value unless it is cast to the desired subtype. This type of casting can introduce errors and is considered bad practice.

Scala code
new LecturesIndex().show().asInstanceOf[LecturesIndex].lectureMethod1
new SectionsIndex().show().asInstanceOf[SectionsIndex].sectionMethod1
showEm(new LecturesIndex).asInstanceOf[LecturesIndex].lectureMethod1 showEm(new SectionsIndex).asInstanceOf[SectionsIndex].sectionMethod1

You can run this code by typing:

Shell
$ sbt "runMain ADT2"

this.type

If the Index.show method declares the return type to be this.type instead of Index, then the appropriate Index subtype is returned.

Scala code
sealed trait Index {
  def show(): this.type = {
    println(this)
    this
  }
}

Now we can chain the show methods with other methods without casting:

Scala code
new LecturesIndex().show().lectureMethod1
new SectionsIndex().show().sectionMethod1

Note that the match expression’s return type is still Index because that is the common supertype of all the return types from each case.

You can run this code by typing:

Shell
$ sbt "runMain ADT3"

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.