Skip to content

Instantly share code, notes, and snippets.

@arturopala
Last active May 5, 2017 09:16
Show Gist options
  • Save arturopala/e35d375a0c390851dfd9cd8ec942240b to your computer and use it in GitHub Desktop.
Save arturopala/e35d375a0c390851dfd9cd8ec942240b to your computer and use it in GitHub Desktop.
Future of Etither monad transformer
object FutureOfEitherT {
type Result[T] = Either[Throwable, T]
implicit class FutureOps[T](val f: Future[T]) extends AnyVal {
implicit def orFail: FutureOfEither[T] =
FutureOfEither(f map Right.apply recover { case NonFatal(e) => Left(e) })
}
final case class FutureOfEither[T](value: Future[Result[T]]) {
def map[S](f: T => S)(implicit executor: ExecutionContext): FutureOfEither[S] = {
val p = Promise[Result[S]]()
value.onComplete {
case f: Failure[_] =>
p complete f.asInstanceOf[Failure[Result[S]]]
case Success(v) => v match {
case Left(failure) => p complete Success(Either.left(failure))
case Right(a) => p complete Success(Right(f(a)))
}
}
FutureOfEither(p.future)
}
def flatMap[S](f: T => FutureOfEither[S])(implicit executor: ExecutionContext): FutureOfEither[S] = {
val p = Promise[Result[S]]()
value.onComplete {
case f: Failure[_] =>
p complete f.asInstanceOf[Failure[Result[S]]]
case Success(v) => v match {
case Left(failure) => p complete Success(Either.left(failure))
case Right(a) => f(a).value.onComplete(v => p complete v)
}
}
FutureOfEither(p.future)
}
def fold[S](f: Result[T] => S): Future[S] = value map f
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment