Scalaz:如何链接未来[列表[A]],选项[A]和未来[选项[A]]并报告单个错误


(for {
  orderId <- ListT(extractParamFromHttp(request).toList) // extractParamFromHttp(request) returns Option[Long]
  order <- ListT(serviceA.retrieve(orderId).map(_.toList))  // serviceA.retrieve(...) returns Future[Option[Order]]
  items <- ListT(serviceB.retrieve(order.id).map(_.toList))  // serviceB.retrieve(...) returns Future[Seq[OrderItem]]
  } yield items).map(...) // convert items to JSON and return as Future[Result]

上面的代码有效,如果给出不正确的输入或缺少输入,当其中任何一个无法获取信息时,它会返回 Nil。我希望使用更具体的错误消息而不是空列表进行响应。例如,如果未找到 HTTP 请求中的参数。我想告诉发件人HTTP 参数丢失或找不到给定的订单 ID。使用 T 或其中之一会有所帮助吗?我应该怎么做?谢谢

更新 1

我使用 Scalaz EitherTEither(来自 Validation 的提示(解决了它,并将我的解决方案粘贴到此处并进行细微更改,

import scalaz._
import Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case class Order(id: Long, desc: String)
case class Vendor(id: Long, name: String)
def extractHeader(valid: Boolean): Option[String] = if (valid) Some("bob") else None
def retrieveVendor(username: String, found: Boolean): Future[Option[Vendor]] = 
  if (found) Future.successful(Some(Vendor(103, "Bob Enterprise"))) else Future.successful(None)
def retrieveOrders(vendorId: Long, found: Boolean): Future[List[Order]] =
  if (found) Future.successful(List(Order(1, "Product 1"), Order(2, "Product 2"))) else Future.successful(List.empty[Order])
def output(result: Future[/[String, AnyRef]]) = result.foreach {
  case -/(s) => println(s"Error: $s")
  case /-(ys @ x :: xs) => println(ys)
  case /-(s) => println(s)
}
def orders(valid: Boolean, userFound: Boolean, vendorFound: Boolean) = for {
  username <- EitherT(Future.successful(extractHeader(valid) /> "Bad headers"))   // Option[String]
  vendor <- EitherT(retrieveVendor(username, userFound).map(_ /> "Bad username")) // Future[Option[Vendor]]
  orders <- EitherT(retrieveOrders(vendor.id, vendorFound).map {                   // Future[Seq[Order]]    
    case Nil => "Bad vendor".left
    case xs => xs.right
  })                                                                              
} yield orders
output(orders(false,false, true).run)

请参阅非 Scalaz 解决方案的答案

更新 2

有关此解决方案的适用形式(如果需要(或更详细的解决方案,请参阅此处

我建议你扔掉 scalaz,它不会给这个用例带来任何价值,只会让你更困惑。未能保持简单通常是此类问题的原因。

def ifExists[T, R](opt: Option[T], err: String)(f: T => Future[R]) = 
  opt.fold(Future.failed(new IllegalArgumentException(err))(f)
for(
   order <- ifExists(extractParamFromHttp(request), "Missing param) {
     serviceA.retrieve(_)
   }
   items <- ifExists(order, "Order not found") { serviceB.retrieve(_) }
   result <- ...
) yield result

这将返回Future[Result],完成后将包含最终结果或指示失败原因的异常。然后,调用方可以使用future.handle...检查异常,或者使用future.transformfuture.onComplete处理成功和失败

最新更新