在Scala中避免深层嵌套的选项级联
发布时间:2020-12-16 19:18:30 所属栏目:安全 来源:网络整理
导读:假设我有三个数据库访问函数foo,bar和baz,每个函数都可以返回Option [A],其中A是某个模型类,并且调用依赖于彼此. 我想按顺序调用这些函数,并且在每种情况下,如果找不到该值,则返回相应的错误消息(无). 我当前的代码如下所示: Input is a URL: /x/:xID/y/:yI
假设我有三个数据库访问函数foo,bar和baz,每个函数都可以返回Option [A],其中A是某个模型类,并且调用依赖于彼此.
我想按顺序调用这些函数,并且在每种情况下,如果找不到该值,则返回相应的错误消息(无). 我当前的代码如下所示: 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 comprehension,我不能给出具体的错误信息,因为我不知道哪一步失败了: (for { x <- foo(xID) y <- bar(yID) z <- baz(zID) } yield { Right(process(x,z)) }).getOrElse(Left("One of the IDs was invalid,but we do not know which one")) 如果我使用map和getOrElse,我最终得到的代码几乎与第一个示例一样嵌套. 这些是更好的结构方法,以避免嵌套,同时允许特定的错误消息? 解决方法
您可以使用正确的投影来使您的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,z) 这仍然有点笨拙,但它具有成为标准库的一部分的优势. 例外是另一种方法,但如果失败的情况很常见,它们会使事情变得缓慢.如果失败真是异常,我只会用它. 也可以使用非本地回报,但这种特殊设置有点尴尬.我认为对Either的正确预测是要走的路.如果你真的喜欢以这种方式工作但是不喜欢把它放在那个地方,那么你可以在各个地方找到一个“右偏的Either”,它默认就像正确的投影(例如ScalaUtils,Scalaz等). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |