Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Making dealing with Future[Either[L, R]] palatable in Scala
package com.thoughtworks.futureeither
import concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import concurrent.duration.FiniteDuration
import java.util.concurrent.TimeUnit
class FutureEither[L, R](private val future: Future[Either[L, R]]) {
def flatMap[R2](block: R => FutureEither[L, R2]): FutureEither[L, R2] = {
val result = future.flatMap {
case Right(r) => block.apply(r).future
case Left(l) => Future.successful(Left(l))
}
new FutureEither(result)
}
def map[R2](block: R => R2): FutureEither[L, R2] = {
val result = future.map {
case Right(r) => Right(block.apply(r))
case Left(l) => Left(l)
}
new FutureEither(result)
}
def recover(block: PartialFunction[L, R]): Future[R] = {
future.map {
case Right(r) => r
case Left(left) => block.apply(left)
}
}
}
object FutureEither {
//bidirectional implicit conversions between Future[Either[L, R]] and FutureEither[L, R]
}
object Example {
abstract sealed class Error {}
case class HeadFellOff() extends Error {}
case class PantsFellOff() extends Error {}
case class WTF() extends Error {}
def returnString: FutureEither[Error, String] = new FutureEither(Future(Right("a string!")))
def takeStringAndReturnInt(string: String): FutureEither[Error, Int] = new FutureEither(Future(Right(10)))
def takeIntAndReturnString(int: Int): FutureEither[Error, String] = new FutureEither(Future(Right("ftw")))
def main(args: Array[String]) {
val result = returnString.flatMap(
takeStringAndReturnInt(_).flatMap(
takeIntAndReturnString(_).map(_.toUpperCase)
)
).recover {
case HeadFellOff() => "head fell off!"
case PantsFellOff() => "pants fell off!"
case unanticipated => s"$unanticipated"
}
println(Await.result(result, FiniteDuration(5, TimeUnit.SECONDS)))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment