Replace Exceptions with Sum Types


You have a function that either returns a value or throws an exception.

Change the function's return type to a disjunction of the original return type and the exception.

We use the following form for our disjunction, called Either:

sealed trait Either[A,B]
case class Left[A,B](x: A) extends Either[A,B]
case class Right[A,B](x: B) extends Either[A,B]

This is similar to Scala's built-in Either type, but with support for for-comprehensions.


Checked exceptions force the developer to wrap their code in try/catch blocks, yielding noisy code that's hard to reason about, hard to combine, and impure in languages where try/catch blocks are not expressions.

Unchecked exceptions allow the developer to forget to wrap their code in try/catch blocks, creating a runtime timebomb.


When encountering an expression that throws an exception, note the types of the exception that it can throw, and the value that it can return. We'll call these A and B, respectively.

Remodel the expression's return type to Either[A,B].

Change any throw e into Left(e).

Change any return b into Right(b).



def div(x: Int, y: Int): Int = x / y
val quotient: Int =
  try {
    div(42, 0)
  } catch {
    case e: ArithmeticException => -999

println(s"quotient: ${quotient}") // quotient: -999


def divE(x: Int, y: Int): Either[Exception, Int] =
  try {
    Right(x / y)
  } catch {
    case e: Exception => Left(e)
val quotientE: Either[Exception,Int] = divE(42, 0)
println(s"quotientE: ${quotientE}") // quotientE: Left(java.lang.ArithmeticException: / by zero)

Right-biased either

implicit class RightBiasedEither[A,B](e: Either[A,B]) {

  def flatMap[C](f: B => Either[A,C]): Either[A,C] =
    e match {
      case Left(a) => Left(a)
      case Right(b) => f(b)

  def map[C](f: B => C): Either[A,C] =
    flatMap { b =>

val quotientE2: Either[Exception,Int] =
  for {
    a <- divE(42, 7)
    b <- divE(a, 0)
  } yield b

println(s"quotientE2: ${quotientE2}") // quotientE2: Left(java.lang.ArithmeticException: / by zero)


quotient: -999
quotientE: Left(java.lang.ArithmeticException: / by zero)
quotientE2: Left(java.lang.ArithmeticException: / by zero)