Phantom types - Make illegal states unrepresentable

HKT HKT
Views

Making illegal states irrepresentable is a powerful technique in (functional) programming. The technique constrains certain operations to specific states, preventing your code from compiling if you attempt an invalid operation. The other less-than-ideal alternative is to check for valid states for operations at runtime.

Let us see a simple example:

sealed trait Status
class Available extends Status
class CheckedOut extends Status
class Book[S <: Status](val title: String) {
  def checkout(implicit ev: S =:= Available): Book[CheckedOut] = {
    new Book[CheckedOut](title)
  }

  def returnBook(implicit ev: S =:= CheckedOut): Book[Available] = {
    new Book[Available](title)
  }
}

Let us see usage:

val fpis = new Book[Available]("Functional Programming in Scala")
val checkedOut = fpis.checkout() // Works
// fpis.returnBook() // Will not compile
// checkedOut.checkout() // Will not compile

That’s the beauty of making illegal states unrepresentable. You cannot bypass the compiler by attempting to check out an already checked-out book. Or return one that is still Available. Since the compiler will not permit such operations in the first place, there is no need to write tests to ensure you are not attempting illegal operations anywhere in your code.

In Scala, the =:= symbol is a type constraint that signifies that the left side is the same type as the right. For example, S =:= Available means that S must be the same type as Available, effectively making the checkout operation usable only for Book[Available] instances.

If you are wondering about Phantom Types, Status is it. In Scala, a phantom type is a type that has no runtime representation but is used to enforce constraints at compile time. And they help make illegal states irrepresentable.

scala