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 => secondResult.map(b => a + b )) // will evaluate to 3
val _: Either[String,Int] = firstResult.flatMap(a => wrongResult.flatmap(b => secondResult.map(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
EitherT[F[_],A,B]
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")
References
