avatar

Andres Jaimes

Future Either

By Andres Jaimes

- 2 minutes read - 277 words

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