scala - 在 Scala 中避免深度嵌套的 Option 级联

标签 scala nested option scala-cats

假设我有三个数据库访问函数 foo , bar , 和 baz可以各自返回Option[A]哪里A是一些模型类,调用相互依赖。

我想按顺序调用这些函数,并且在每种情况下,如果找不到值( None ),则返回适当的错误消息。

我当前的代码如下所示:

Input is a URL: /x/:xID/y/:yID/z/:zID

foo(xID) match {
  case None => Left(s"$xID is not a valid id")
  case Some(x) =>
    bar(yID) match {
      case None => Left(s"$yID is not a valid id")
      case Some(y) =>
        baz(zID) match {
          case None => Left(s"$zID is not a valid id")
          case Some(z) => Right(process(x, y, z))
        }
    }
}

可以看出,代码嵌套严重。

如果相反,我使用 for理解,我不能给出具体的错误信息,因为我不知道哪一步失败了:
(for {
  x <- foo(xID)
  y <- bar(yID)
  z <- baz(zID)
} yield {
  Right(process(x, y, z))
}).getOrElse(Left("One of the IDs was invalid, but we do not know which one"))

如果我使用 mapgetOrElse ,我最终得到的代码几乎与第一个示例一样嵌套。

这些是否有更好的方法来构建它以避免嵌套,同时允许特定的错误消息?

最佳答案

您可以获取您的 for通过使用正确的投影循环工作。

def ckErr[A](id: String, f: String => Option[A]) = (f(id) match {
  case None => Left(s"$id is not a valid id")
  case Some(a) => Right(a)
}).right

for {
  x <- ckErr(xID, foo)
  y <- ckErr(yID, bar)
  z <- ckErr(zID, baz)
} yield process(x,y,z)

这仍然有点笨拙,但它具有成为标准库的一部分的优势。

异常是另一种方法,但如果失败案例很常见,它们会大大减慢速度。如果失败真的很特别,我只会使用它。

也可以使用非本地返回,但对于这种特定设置来说有点尴尬。我认为 Either 的正确预测是要走的路。如果您真的喜欢这种方式工作但不喜欢放置 .right到处都是,你可以在很多地方找到一个“偏右的任一个”,默认情况下它会像正确的投影一样(例如 ScalaUtils ,Scalaz 等)。

关于scala - 在 Scala 中避免深度嵌套的 Option 级联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28884742/

相关文章:

option - 使用 YQL 获取金融期权数据

scala - 在语句中混合 Scala Option 和常规变量

java - Map<String, Map<String, Boolean>> myMap = new HashMap<String,HashMap<String,Boolean>>();

javascript - 访问 Angular 工厂中的嵌套函数闭包

java - 如何在 Java 上列出倒三角形上的数字

scala - 比较 Scala 泛型函数中泛型类型的值

jquery - 如果用户在提交时未更改<select> <option>,如何添加消息

string - 如何在 Scala 中按字符串拆分字符串

java - 每当我关闭框架时,复制到剪贴板的数据就会丢失

Scala:使用伴随对象的父对象的 protected 方法