Future Either
This implementation is based on the work of Malcolm, and exists because it provides the cleanest use of similar monads.
The base monad wraps an Either class in a Future class:
1import scala.concurrent.{ExecutionContext, Future}
2import scala.util.{Failure, Success, Try}
3
4final case class FutureEither[+E, +A](value: Future[Either[E, A]]) {
5
6 def map[B](f: A => B)(implicit ec: ExecutionContext): FutureEither[E, B] =
7 FutureEither(value map {
8 case Right(a) => Right(f(a))
9 case Left(e) => Left(e)
10 })
11
12 def flatMap[EE >: E, B](f: A => FutureEither[EE, B])(implicit ec: ExecutionContext): FutureEither[EE, B] =
13 FutureEither(value flatMap {
14 case Right(a) => f(a).value.map {
15 case Right(a) => Right(a)
16 case Left(e) => Left(e)
17 }
18 case Left(e) => Future.successful(Left(e))
19 })
20
21}
The companion object defines multiple functions to instantiate a FutureEither from other monads.
1object FutureEither {
2
3 def successful[A](a: A): FutureEither[Nothing, A] = FutureEither(Future.successful(Right(a)))
4
5 def failed[E](e: E): FutureEither[E, Nothing] = FutureEither(Future.successful(Left(e)))
6
7 def fromEither[E, A](v: Either[E, A]): FutureEither[E, A] = FutureEither(Future.successful(v))
8
9 def fromTry[A](v: Try[A]): FutureEither[Throwable, A] = FutureEither(Future.successful(v.toEither))
10
11 def fromFuture[A](fa: Future[A])(implicit ec: ExecutionContext): FutureEither[Throwable, A] =
12 FutureEither(fa.transform {
13 case Success(a) => Success(Right(a))
14 case Failure(e) => Success(Left(e))
15 })
16
17 def sequence[A](seq: Seq[FutureEither[Throwable, A]])(implicit ec: ExecutionContext): FutureEither[Throwable, Seq[A]] =
18 FutureEither(Future.sequence {
19 seq map { fe => fe.value }
20 } transform {
21 case Success(s) =>
22 if (s.forall(_.isRight)) Success(Right(s.flatMap(_.toSeq)))
23 else Success(Left(s.filter(_.isLeft).flatMap(_.toSeq).head.asInstanceOf[Throwable]))
24 case Failure(e) => Success(Left(e))
25 })
26
27}
References
- Malcolm. 2020, September 06. The problem with EitherT. Beyond the lines. http://www.beyondthelines.net/programming/the-problem-with-eithert/
- Scala with cats IO/Either instead of Future/Exceptions
- ZIO
- What do “effect” and “effectful” mean in functional programming?