Right Biased either in Scala

We are using Scala for one of our clients. However, they didn't want to involve using fancy/functional libraries such as scalaZ which can be hard for newcomers. However we really needed validation tools such as one in scalaZ . After analyzing our requirements, we realized that we don't need error accumulation and decided go further with Scala's Either class. Unfortunately, Either is unbiased, which means there is no implication which side of Either is an Error or a Success.

Let see an example

Assuming we have following definitions,

case class Error(msg: String)
type Validation[+A] = Either[Error, A]

val result1: Validation[Int] = Right(42)
val result2: Validation[String] = Right("Is the Answer")
val result3: Validation[Int] = Left(Error("Ooops Failed"))

If we want to use them in a for-comprehension as follows,

val response = for {
  res1 <- result1
  res2 <- result2
} yield s"${res1.toString} $res2"

We will hit the following compile error:

error: value flatMap is not a member of Validation[Int]
    res1 <- result1

Since compiler needs map and flatMap methods we need to take Right Projection of it as follows. This makes Either class rightly-biased.

val response = for {
  res1 <- result1.right
  res2 <- result2.right
} yield s"${res1.toString} $res2"
response: scala.util.Either[Error,String] = Right(42 Is the Answer)

This will work, too

val response = for {
 res1 <- result1.right
 res2 <- result2.right
 res3 <- result3.right
} yield s"${res1.toString} $res2"

However, when we try to use some fancy stuff such as intermediate for-comprehension values, we will hit some compiler errors.

Example

val response = for {
  res1 <- result1.right
  res2 <- result2.right
  foobar = 1
  res3 <- result3.right
} yield s"${res1.toString} $res2"
error: value flatMap is not a member of Serializable with Product with scala.util.Either[Error,(String, Int)]
    res2 <- result2.right

Somehow compiler lost that we have a Right Projection and then it treats Either again as an unbiased data structure. It would be cool if we had a way to give compiler a hint about this.

Luckily, we have implicits and we can make an Either value a right biased one with following implicit conversion.

object Implicits {
  implicit class RightBiasedValidation[+A](val e: Validation[A]) extends AnyVal {
    def foreach[U](f: A => U): Unit = e.right.foreach(f)
    def map[B](f: A => B): Validation[B] = e.right.map(f)
    def flatMap[B](f: A => Validation[B]) = e.right.flatMap(f)
  }
}

Now we can run fancy stuff within for-comprehensions for our little framework.

import Implicits._
val response = for {
  res1 <- result1.right
  res2 <- result2.right
  foobar = 1
  res3 <- result3.right
} yield s"${res1.toString} $res2"
response: scala.util.Either[Error,String] = Left(Error(Ooops Failed))

A Real Life Example

For those who says Talk is cheap show me the code people here is an example. You can see how the code becomes more flat and readable.