avatar

Andres Jaimes

Future Either

By Andres Jaimes

- 2 minutes read - 235 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:

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

final case class FutureEither[+E, +A](value: Future[Either[E, A]]) {

  def map[B](f: A => B)(implicit ec: ExecutionContext): FutureEither[E, B] =
    FutureEither(value map {
      case Right(a) => Right(f(a))
      case Left(e) => Left(e)
    })

  def flatMap[EE >: E, B](f: A => FutureEither[EE, B])(implicit ec: ExecutionContext): FutureEither[EE, B] =
    FutureEither(value flatMap {
      case Right(a) => f(a).value.map {
        case Right(a) => Right(a)
        case Left(e) => Left(e)
      }
      case Left(e) => Future.successful(Left(e))
    })

}

The companion object defines multiple functions to instantiate a FutureEither from other monads.

object FutureEither {

  def successful[A](a: A): FutureEither[Nothing, A] = FutureEither(Future.successful(Right(a)))

  def failed[E](e: E): FutureEither[E, Nothing] = FutureEither(Future.successful(Left(e)))

  def fromEither[E, A](v: Either[E, A]): FutureEither[E, A] = FutureEither(Future.successful(v))

  def fromTry[A](v: Try[A]): FutureEither[Throwable, A] = FutureEither(Future.successful(v.toEither))

  def fromFuture[A](fa: Future[A])(implicit ec: ExecutionContext): FutureEither[Throwable, A] =
    FutureEither(fa.transform {
      case Success(a) => Success(Right(a))
      case Failure(e) => Success(Left(e))
    })

  def sequence[A](seq: Seq[FutureEither[Throwable, A]])(implicit ec: ExecutionContext): FutureEither[Throwable, Seq[A]] =
    FutureEither(Future.sequence {
      seq map { fe => fe.value } 
    } transform {
      case Success(s) =>
        if (s.forall(_.isRight)) Success(Right(s.flatMap(_.toSeq)))
        else Success(Left(s.filter(_.isLeft).flatMap(_.toSeq).head.asInstanceOf[Throwable]))
      case Failure(e) => Success(Left(e))
    })

}

References