18

我有一个应用程序可以对不同的后端系统进行大量调用,并希望使用 for-comprehensions 来简化跨后端系统的流程。

我希望将 EitherT (scalaz) 和 Future (scala 2.10) 结合起来,这样我就可以捕获第一个潜在错误(它是未来或后端系统问题)并向最终用户返回适当的消息。我已经快速浏览了一个 scalaz Validation,但是对于捕获第一个错误而不是所有错误的建议是使用 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)
4

2 回答 2

6

您需要为 Future 导入或提供 Functor 实例。我建议使用scalaz-contrib项目中的那个。-contrib 是一个单独的项目,由在 scalaz 上工作的同一个人进行。Future 实例位于此包中,而不是 scalaz-core,因为 scalaz-core 目前保持 scala 2.9 和 2.10 之间的兼容性。

于 2013-07-25T00:40:52.367 回答
1

查看在isRightEitherT 上定义的签名:

def isRight(implicit F: Functor[F]): F[Boolean]

在您的情况下,它期望使用您的 EitherT 的类型参数参数化的 Functor。Scalaz 没有为 Future 类型提供隐式 Functor,您需要按照这个模型编写自己的:

http://scalaz.github.io/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Functor.scala.html

请注意每种支持的类型的所有隐式定义。

于 2013-07-25T00:26:11.137 回答