Why I don't like Scala's "Either"
Tags: scala, functional
Published: 2019-07-22 13:23:11

### The problem

Let's say we are assigned to develop a function that validates usernames. For the sake of the example, the function only checks whether the provided ``String`` value is between four and ten characters long.

A first draft of the function could be something like that:
```scala
def validateUsername(value: String): Boolean = {
  val length = value match {
    case s: String => s.length
    case _         => 0
  }

  length >= 4 && length <= 10
}
```
This implementation returns ``true`` for all valid usernames, but has two unfortunate shortcomings:

1. The fact that the function returns a ``Boolean`` does not encourage to use it in a purely functional manner.
2. If a username is invalid, the caller does not get any information about why.

The first problem can be easily fixed by slightly modifying implementation:

```scala
def validateUsername(value: String): Option[String] = value match {
  case s: String if (s.length >= 4 && s.length <= 10) => Some(value)
  case _                                              => None
}
```

To be able to remedy the second shortcoming, we need to elaborate in more detail.

### That's why ``Option`` is not a good option ...
While [``Option``](https://www.scala-lang.org/api/current/scala/Option.html) allows the use of ``validateUsername`` in a functional style, it does not provide information about why the validation failed.

What we need is a way to return the validated username, or an error indicating why the validation failed. Luckily, Scala offers several solutions to this problem - let's take a closer look at some of them.

###... and ``Either`` neither.
With [``scala.util.Either``](https://www.scala-lang.org/api/current/scala/util/Either.html) the _Scala Standard Library_ offers a container that wraps two (different) types.

Let's modify our example so that it uses ``Either`` as the return type:

```scala
def validateUsername(value: String): Either[String, String] = {
  val min = 4    // minimum legnth for username
  val max = 10   // maximum lenght for username

  val length = value match {
    case s: String => s.length
    case _         => 0
  }

  if (length >= min && length <= max) {
    Right(value)
  } else if (length < min) {
    Left("'value' is too short.")
  } else {
    Left("'value' is too long.")
  }
}
```

Now, let's use ``validateUsername`` in a simple example:
```scala
validateUsername("user") match {
  case Right(u) => println(s"A valid username: '$u'");
  case Left(r)  => println(s"Invalid username; reason: '$r'")
}
```
This does not look too bad, but as the title suggests: _I strongly dislike Scala's ``Either``_, and I am going to explain to you why.

To fully understand what I don't like about Either let's have a brief look at its implementation.

Much like ``Option``, ``Either`` is a _sealed abstract class_ with two concrete implementations (``Left`` and ``Right``), but with the important difference of taking two type parameters instead of one. I don't have so much a problem with the implementations of the derived classes as with their naming.

To me, there is not much semantic to ``Left`` and ``Right``. Of course, the names refer to the position of the respective type parameters in the declaration, but that's about all. One does not need to suffer from dyslexia to get confused. Which type indicates that the function returned without an error? Is it ``Left`` because it is declared first or is it ``Right`` because it spells like "right"?

How nice would it be to have result types that indicate doubtlessly whether a function returned OK or with an error? Let's see what else the Scala Standard Library has to offer to reflect our desires.

### Let's ``Try`` something else
[``scala.util.Try``](https://www.scala-lang.org/api/current/scala/util/Try.html) was introduced in Scala 2.10 and is meant to perform Exception prone operations without the need to care about explicit exception-handling.
```scala
import scala.util.{Try, Success, Failure}

Try(1 / 0) match {
  case Success(s) => println(s"Result: $s")
  case Failure(x) => println(s"Exception: $x")
}
```
``Try`` works fine with exceptions but can we use it as a return type for our ``validateUsername`` function? We can but this would make the implementation somehow awkward and less concise:

##### 1. Throwing an exception if the validation fails
```scala
import scala.util.{Try, Success, Failure}

def validateUsername(value: String): Try[String] = {
  val min = 4 // minimum legnth for username
  val max = 10 // maximum lenght for username

  val length = value match {
    case s: String => s.length
    case _         => 0
  }

  if (length < min) {
    Failure(new Exception("'value' is too short."))
  } else if (length > max) {
    Failure(new Exception("'value' is too long."))
  } else {
    Success(value)
  }
}

validateUsername("username") match {
  case Success(username) => println(s"Valid username: $username")
  case Failure(ex)       => println(s"Invalid: '${ex.getMessage}'.")
}

```
Here we deliberately throw an exception if the validation of the username fails. This is not a good idea since exceptions should only be thrown in exceptional situations and should never be an expected and valid "result" of the invocation of a function.

##### 2. ``Try`` as result type
```scala
import scala.util.{Try, Success, Failure}

def validateUsername(value: String): Try[String] = {
  val min = 4 // minimum legnth for username
  val max = 10 // maximum lenght for username

  val length = value match {
    case s: String => s.length
    case _         => 0
  }

  if (length < min) {
    Failure(new Exception("'value' is too short."))
  } else if (length > max) {
    Failure(new Exception("'value' is too long."))
  } else {
    Success(value)
  }
}

validateUsername("username") match {
  case Success(username) => println(s"Valid username: $username")
  case Failure(ex)       => println(s"Invalid: '${ex.getMessage}'.")
}
```
In the second example, we do not throw but create exception instances because ``Failure`` requires an exception object. Creating new instances of an exception without throwing them immediately is only acceptable in rare cases and if we come across situations where instances of exceptions are created but not thrown we should be alarmed.

What have we learned so far? 
1. ``Option`` does not work since it wraps only one type and this type should represent a valid result.
2. ``Either`` can wrap two types but the naming of its concrete implementations ``Left`` and ``Right`` suffer from a lack of semantics.
3. Throwing exceptions in the case of a failed validation or using ``Try`` as a result type is not a good idea either.

So does that mean we are out of luck? As for Scala 2.13, the Standard Library does not provide an appropriate type to solve our problem. If we stopped here I actually would suggest using ``Either`` since it is the smallest evil there.

### A Rust-like ``Result`` type

But wait, I am not stopping just there. Scala is an ever-evolving project and so I propose a Rust-like ``Result`` type:
```scala
sealed abstract class Result[+A, +B]

final case class Success[+A, +B](success: A) extends Result[A, B]
final case class Failure[+A, +B](failure: B) extends Result[A, B]

```
Already this simple, stub-like implementation would make our example more concise:
```scala
def validateUsername(value: String): Result[String, String] = {
  val min = 4 // minimum legnth for username
  val max = 10 // maximum lenght for username

  val length = value match {
    case s: String => s.length
    case _         => 0
  }

  if (length >= min && length <= max) {
    Success(value)
  } else if (length < min) {
    Failure("'value' is too short.")
  } else {
    Failure("'value' is too long.")
  }
}

validateUsername("usr") match {
  case Success(u) => println(s"A valid username: '$u'");
  case Failure(r) => println(s"Invalid username; reason: '$r'")
}
```
If we want to use ``Result`` in a more sophisticated way, we can add a ``map`` function:
```scala
sealed abstract class Result[+A, +B] {
  def map[AA](f: A => AA): Option[AA] = this match {
    case Success(a) => Some(f(a))
    case Failure(_) => None
  }
}
```
This would allow us to only take care of the happy path:
```scala
validateUsername("username").map(x => x).foreach(createUserAccount(_));
```
In my opinion, there is a great potential to a ``Result`` type in Scala which would make error handling more concise and easier to understand.