scala - Combinando EitherT y Future
scalaz for-comprehension (2)
Debe importar o proporcionar una instancia de Functor para el futuro. Recomiendo usar el del proyecto scalaz-contrib . -contrib es un proyecto separado en el que trabajaron las mismas personas que trabajan en scalaz. Las instancias Futuras están en este paquete en lugar de scalaz-core porque scalaz-core, por ahora, mantiene la compatibilidad entre scala 2.9 y 2.10.
Tengo una aplicación que hace muchas llamadas a diferentes sistemas de back-end, y espero utilizar las comprensiones para simplificar el flujo del proceso a través de los sistemas de back-end.
Estoy buscando combinar EitherT (scalaz) y Future (scala 2.10) para poder capturar el primer error potencial (donde es un problema futuro o del sistema backend) y devolver un mensaje apropiado al usuario final. He echado un vistazo rápido a Validation scalaz pero la recomendación para capturar el primer error y no todos los errores es usar EitherT.
Estoy probando un ejemplo simple en REPL primero, pero obtengo el siguiente error
error: no se pudo encontrar el valor implícito para el parámetro 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
}
Todavía soy nuevo tanto en scala como en scalaz, por lo que cualquier puntero sería genial.
** Actualización **
Al incluir scalaz-contrib en base a la sugerencia de @stew, ahora tengo una versión actualizada que muestra las comprensiones con la combinación de EitherT y Future que muestra diferentes casos de uso simple éxito de back-end, falla de backend y falla futura
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)
Mire la firma de isRight
definida en EitherT:
def isRight(implicit F: Functor[F]): F[Boolean]
Está esperando un Functor parametrizado con el parámetro de tipo de su EitherT, en su caso Futuro. Scalaz no proporciona un Functor implícito para el tipo Future, debe escribir el suyo siguiendo este modelo:
http://scalaz.github.io/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Functor.scala.html
Observe todas las definiciones implícitas para cada tipo soportado.