/maybe

an implementation of the Maybe monad in Scala

Primary LanguageScalaMIT LicenseMIT

maybe

License: MIT

This is a barebones implementation of Scala's own Option[T] library type, conveniently named Maybe[T]. The name comes from the maybe monad, which also happens to be the the name of the type that is analogous to Option[T] in Haskell.

Why Maybe?

Optional types provide an ergonomic way to enforce null-safety in many languages. The burden of remembering to check whether some value is null or not before performing some computation on it is lifted from the programmer to the compiler (which then bugs the programmer to explicitly handle it). Without an optional type, we could have the following disaster

public int sneakyFactorial(int n) {
  if (n > 10) {
    return null;
  }
  if (n == 0) {
    return 1;
  }
  return n * sneakyFactorial(n - 1);
}

int someBigNum = sneakyFactorial(11);
// using someBigNum might lead to a NullPointerException if you
// don't check for null, the compiler also might not warn you.

We can rewrite the method above as a function that uses the Maybe[T] type in Scala

def sneakyFactorial(n: Int): Maybe[Int] = n match {
  case num if num > 10 => Nothing()
  case num if num == 1 => Just(1)
  case num => sneakyFactorial(num - 1).map(_ * num)
}

val maybeSomeBigNum = sneakyFactorial(11)

// using maybeSomeBigNum without destructuring it into a Just[T] or a Nothing[T] will
// cause a compiler error
maybeSomeBigNum * 2 // compiler error

// Use explicit pattern matching
maybeSomeBigNum match {
  case Just(n) => n * 2 // this is fine
  case Nothing() => // handle the "null" case here
}

Note that we have access to all the standard functions that are able to be called on sequences. This is because one can effectively think of a Maybe[T] as a unary list

// fine
maybeSomeBigNum.map(_ * 2).getOrElse(-1)

// also fine
maybeSomeBigNum.fold(-1)(_ * 2)

This is a rather contrived use case, but it demonstrates the safety gained from replacing nullable values with a Maybe[T] type. It can effectively eliminate a class of runtime errors caused by null pointers by forcing a check at compile-time to make sure they are handled.

Dependencies

Working with maybe

maybe is an sbt project so it's relatively easy to spin up

  • Run sbt to start the build server
    • compile to build the project
    • test to run all tests under the test directory (spec files)
    • clean to remove all generated code