我有一个应用程序可以对不同的后端系统进行大量调用,并希望使用for-comprehensions来简化后端系统的流程。
我希望将EitherT(scalaz)和Future(scala 2.10)结合起来,这样我就可以捕获第一个潜在错误(其未来或后端系统问题),并向最终用户返回相应的消息。我已经快速查看了scalaz验证,但是建议捕获第一个错误而不是所有错误都是使用EitherT。
我首先在REPL中尝试一个简单的例子,但是我收到了以下错误
错误:找不到参数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
}
我对scala和scalaz都很陌生,所以任何指针都会很棒。
**更新**
通过包含基于@stew建议的scalaz-contrib,我现在有了一个更新版本,它显示了对EitherT和Future组合的理解,显示了后端成功,后端失败和未来失败的不同简单用例
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)
答案 0 :(得分:6)
您需要为Future导入或提供Functor实例。我建议使用scalaz-contrib项目中的那个。 -contrib是一个由scalaz工作的同一个人工作的独立项目。 Future实例在这个包中而不是scalaz-core,因为scalaz-core现在保持了scala 2.9和2.10之间的兼容性。
答案 1 :(得分:1)
查看EitherT上定义的isRight
签名:
def isRight(implicit F: Functor[F]): F[Boolean]
期望使用您的EitherT的类型参数参数化Functor,在您的情况下为Future。 Scalaz没有为Future类型提供隐式Functor,您需要按照此模型编写自己的Functor:
http://scalaz.github.io/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Functor.scala.html
请注意每种受支持类型的所有隐式defs。