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:
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
- 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?