Scala - Futures 和 Eithers

Scala - Futures and Eithers

这个帖子让我了解了如何构建我的代码:Scala-way to handle conditions in for-comprehensions?

问题部分:

// First create the JSON
val resultFuture: Future[Either[Failure, JsResult]] = for {
  userRes <- userDao.findUser(userId)
  user    <- userRes.withFailure(UserNotFound).right
  authRes <- userDao.authenticate(user)
  auth    <- authRes.withFailure(NotAuthenticated).right
  goodRes <- goodDao.findGood(goodId)
  good    <- goodRes.withFailure(GoodNotFound).right
  checkedGood <- checkGood(user, good).right
} yield renderJson(Map("success" -> true)))

这是我不明白的台词:

user    <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)

userRes.withFailure(UserNotFound).right映射到userDao.authenticate(user)。这将创建一个新的 Either,右边有一个 Future,对吗?

如何

val resultFuture: Future[Either[Failure, JsResult]]

属于它的类型。我认为应该有另一个未来而不是 JsResult。 谁能给我解释一下?

编辑:由于cmbaxter和Arne Claassen证实了这一点,新的问题是:我应该如何编写这段代码,所以它看起来并不难看,但是整洁有序?

我相信您收到的答案不必要地将 Either 混入其中,而 Future 已经完全能够传达失败。您缺少的主要内容是一种从 Option 获取选项值而不显式抛出异常的方法。

我建议您将 Failures 对象更改为以下内容:

object Failures {

  sealed trait Failure extends Exception

  // Four types of possible failures here
  case object UserNotFound extends Failure

  case object NotAuthenticated extends Failure

  case object GoodNotFound extends Failure

  case object NoOwnership extends Failure

  // Put other errors here...

  // Converts options into Futures
  implicit class opt2future[A](opt: Option[A]) {
    def withFailure(f: Failure) = opt match {
      case None => Future.failed(f)
      case Some(x) => Future.successful(x)
    }
  }
}

现在您可以将 Future[Option[A]] 映射到 Future[A] 并指定失败条件,从而产生这样的 for 理解:

def checkGood(user: User, good: Good) =
  if (checkOwnership(user, good))
    Future.successful(good)
  else
    Future.failed(NoOwnership)

val resultFuture: Future[JsResult] = for {
  userOpt <- userDao.findUser(userId)
  user <- userOpt.withFailure(UserNotFound)
  authOpt <- userDao.authenticate(user)
  auth <- authOpt.withFailure(NotAuthenticated)
  goodOpt <- goodRes.withFailure(GoodNotFound)
  checkedGood <- checkGood(user, good)
} yield renderJson(Map("success" -> true))))

现在您有了一个 Future[JsResult],您可以将失败的场景映射到您想要的输出,而成功的场景就是 JsResult。希望您在一个异步框架中使用它,该框架希望您为它提供未来,并有自己失败的未来到错误响应映射(例如 Play!)。