How does EitherT saves you from complexity of Either

Reading Time: 3 minutes

In Scala Future[A], Option[T] and Either[A, B] is very useful and most widely used types. Very often we want to combine them, but a Future[Either[A, B]] is kind of awkward to handle.

Before going further let’s take a look at What  Either is and what problem we are facing ?? An either can be left or a right value, it always wraps another value left or the right to give context to the value and we can apply map, flat map etc on it. And it is  also a monad

Let’s take a example here

val eitherValue:Either[String,Int]  
val totallyRight:Either[String,Int] = Right(1) 
val somethingWrong:Either[String,Int] = Left(“something is not right”)
def doSomething(i:Int):Either[String,Int] = if (i!=0) Right(i) else Left(“Wrong Input”)
val _: Either[String,Int] = doSomething(1).map(_ + 1 ) //Right(2)
val _: Either[String,Int] = doSomething(0).map(_ + 1 ) //Left(“Wrong Input”)

Now lets see how we can perform Multiple operation on Either

val firstResult  = doSomething(1)
val secondResult = doSomething(2)
val wrongResult  = doSomething(0)
val _: Either[String,Int] = firstResult.flatMap(a => => a + b )) // will evaluate to 3
val _: Either[String,Int] = firstResult.flatMap(a => wrongResult.flatmap(b => => a + b + c ))) // will evaluate Wrong Input

We can also chain Either like below

val result: Either[String,Int] = for {
a <- firstResult
b <- secondResult
} yield a+b //right(3)
val result: Either[String,Int] = for {
a <- firstResult
b <- secondResult
c <- wrongResult
} yield a+b+c          // left(Wrong Input)

You may have already noticed that either lend themselves well to error handling we have already seen how you are able to compose multiple either together and evaluate them only when you have right

Anytime we encounter the left we can shortcircuit our processing instead of a handling error.

Writing such code is not that simple and becomes a complex task for us

Where we can directly use

val myEither:EitherT[Future,String,Int] = EitherT(Future(Right(42)))

Cats come with EitherT datatype, which is a monad transformer for Either. EitherT is a data type offered by cats

EitherT[F[_], A, B] is a lightweight wrapper for F[Either[A, B]] that makes it easy to compose Either s and F s together


F[ _] are monad can be List,Future,Option etc here for now we are going to use it as Future. A is left value and B is the right value

When we do myEitherT.value we will get the same type of Future[Either[String,Int]]

def checkNumber(i:Int):EitherT[Future,Error,Int]
Val _: Either[Future,Error,Int]  = for {
A <- checkNumber(2)
B <- checkNumber(A)
}yield A+B

From A or B to EitherT[F, A, B]

To obtain a left version we use EitherT.leftT and for the right version of EitherT when using EitherT.rightT

val number: EitherT[Option, String, Int] = EitherT.rightT(5)
val error: EitherT[Option, String, Int] = EitherT.leftT("Not a number")

From F[A] or F[B] to EitherT[F, A, B]

Similarly, use EitherT.left and EitherT.right to convert an F[A] or an F[B] into an EitherT. It is also possible to use EitherT.liftF as an alias for EitherT.right

val number0: Option[Int] = Some(5)
val number: EitherT[Option, String, Int] = EitherT.right(number0)

From Either[A, B] or F[Either[A, B]] to EitherT[F, A, B]

Use EitherT.fromEither to lift a value of Either[A, B] into EitherT[F, A, B]. An F[Either[A, B]] can be converted into EitherT using the EitherT constructor.

val numberE: Either[String, Int] = Right(100)
val errorE: Either[String, Int] = Left("Not a number")
val numberET: EitherT[List, String, Int] = EitherT.fromEither(numberE)
val errorET: EitherT[List, String, Int] = EitherT.fromEither(errorE)

From Option[B] or F[Option[B]] to EitherT[F, A, B]

An Option[B] or an F[Option[B]], along with a default value, can be passed to EitherT.fromOption and EitherT.fromOptionF, respectively, to produce an EitherT.

val myOption: Option[Int] = None
val myOptionET = EitherT.fromOption[Future](myOption, "option not defined")