Combining EitherT and Future

for-comprehension, future, scala, scalaz, scalaz7

Solution

You need to either import or provide a Functor instance for Future. I recommend using the one from the scalaz-contrib project. -contrib is a separate project worked on by the same people that work on scalaz. The Future instances are in this package instead of scalaz-core because scalaz-core, for now, maintains compatibility between scala 2.9 and 2.10.

Problem

I have an app that does a lot of calls to different backend systems, and hoping to use for-comprehensions to simplify the process flow across the backend systems. I'm looking to combine EitherT (scalaz) and Future (scala 2.10) so I can capture the first potential error (where its a future or backend system issue) and return an appropriate message to the end user. I've had a quick look a scalaz Validation but the recommendation for capturing first error and not all errors is to use EitherT. I'm trying a simple example in REPL first, however I'm getting the following error error: could not find implicit value for parameter F: scalaz.Functor[scala.concurrent.Future] ``` import scala.concurrent._ import scalaz._ import Scalaz._ import ExecutionContext.Implicits.global type EitherFuture[+A] = EitherT[Future, String, A] def method1Success : EitherFuture[Int] = { println("method 1 success") EitherT { Future { 1.right } } } def method2Failure : EitherFuture[Int] = { println("method 2 failure") EitherT { Future { "fail".left } } } val m1 = method1Success // problem m1.isRight // problem def methodChain1 = { for { a <- method1Success b <- method2Failure } yield b } ``` I'm still new to both scala and scalaz so any pointers would be great. ** Update ** By including scalaz-contrib based on @stew suggestion I now have an updated version that shows for-comprehensions with the combined EitherT and Future showing different simple use cases backend success, a backend failure, and a future failure ``` import scala.concurrent._ import scalaz._ import Scalaz._ import ExecutionContext.Implicits.global import scalaz.contrib._ import scalaz.contrib.std._ import scala.concurrent.duration._ type EitherFuture[+A] = EitherT[Future, String, A] // various methods that mimic success or different failures def methodBackendSuccess : EitherFuture[Int] = { println("method backend success") EitherT { Future {1.right} } } def methodBackendFailure : EitherFuture[Int] = { println("method backend failure") EitherT { Future { "fail".left} } } def methodFutureFailure : EitherFuture[Int] = { println("method future failure") EitherT { Future.failed(new Exception("future failed")) } } // different combinations for for-comprehensions def methodChainBackendSuccess = { for { a <- methodBackendSuccess b <- methodBackendSuccess c <- methodBackendSuccess } yield c } def methodChainBackendFailure = { for { a <- methodBackendSuccess b <- methodBackendFailure c <- methodBackendSuccess } yield c } def methodChainFutureFailure = { for { a <- methodBackendSuccess b <- methodFutureFailure c <- methodBackendSuccess } yield c } // process results for different chain methods def processOutcome(chainMethod: => EitherFuture[Int]):Int = try { val x = Await.result(chainMethod.run, 30 seconds) x.toEither match { case Left(l) => { println("Backend failure <" + l + ">") -1 } case Right(r) => { println("Backend success <" + r + ">") r } } } catch { case e: Exception => { println("Future error <" + e.getMessage + ">" ) -99 } } // run tests val backendSuccess = processOutcome(methodChainBackendSuccess) val backendFailure = processOutcome(methodChainBackendFailure) val futureFailure = processOutcome(methodChainFutureFailure) ```

Original source