另一个Option对象中的Scala Option对象

psi*_*yev 8 monads scala option scalaz

我有一个模型,它有一些Option字段,其中包含另一个Option字段.例如:

case class First(second: Option[Second], name: Option[String])
case class Second(third: Option[Third], title: Option[String])
case class Third(numberOfSmth: Option[Int])
Run Code Online (Sandbox Code Playgroud)

我从外部JSON收到这些数据,有时这些数据可能包含null,这就是这种模型设计的原因.

所以问题是:获得最深领域的最佳方法是什么?

First.get.second.get.third.get.numberOfSmth.get
Run Code Online (Sandbox Code Playgroud)

上面的方法看起来很丑陋,如果其中一个对象是None,可能会导致异常.我正在寻找Scalaz lib,但没有找到更好的方法来做到这一点.

有任何想法吗?提前致谢.

Rég*_*les 17

解决方案是使用Option.mapOption.flatMap:

First.flatMap(_.second.flatMap(_.third.map(_.numberOfSmth)))
Run Code Online (Sandbox Code Playgroud)

或等效的(请参阅本答复末尾的更新):

First flatMap(_.second) flatMap(_.third) map(_.numberOfSmth)
Run Code Online (Sandbox Code Playgroud)

这返回一个Option[Int](假设numberOfSmth返回一个Int).如果调用链中的任何选项是None,结果将是None,否则它将返回的值在Some(count)哪里.countnumberOfSmth

当然,这会很快变得丑陋.因此,scala支持将理解作为语法糖.以上可以改写为:

for { 
  first <- First
  second <- first .second
  third <- second.third
} third.numberOfSmth
Run Code Online (Sandbox Code Playgroud)

哪个可以说更好(特别是如果你还不习惯看到map/ flatMap无处不在,使用scala一段时间后肯定就是这种情况),并在引擎盖下生成完全相同的代码.

有关更多背景信息,您可以查看另一个问题:Scala的收益率是多少?

更新:感谢Ben James指出flatMap是关联的.换句话说x flatMap(y flatMap z)))是一样的x flatMap y flatMap z.虽然后者通常不短,但它具有避免任何嵌套的优点,这更容易遵循.

这里是REPL中的一些例子(4个样式是等价的,前两个使用flatMap嵌套,另外两个使用flatMap的扁平链):

scala> val l = Some(1,Some(2,Some(3,"aze")))
l: Some[(Int, Some[(Int, Some[(Int, String)])])] = Some((1,Some((2,Some((3,aze))))))
scala> l.flatMap(_._2.flatMap(_._2.map(_._2)))
res22: Option[String] = Some(aze)
scala> l flatMap(_._2 flatMap(_._2 map(_._2)))
res23: Option[String] = Some(aze)
scala> l flatMap(_._2) flatMap(_._2) map(_._2)
res24: Option[String] = Some(aze)
scala> l.flatMap(_._2).flatMap(_._2).map(_._2)
res25: Option[String] = Some(aze)
Run Code Online (Sandbox Code Playgroud)

  • 你不需要使用丑陋的嵌套:由于`flatMap`的相关性,`flatMap(b flatMap c)`相当于`flatMap b flatMap c` (3认同)

om-*_*nom 10

没有必要使用scalaz:

for { 
  first  <- yourFirst
  second <- f.second
  third  <- second.third
  number <- third.numberOfSmth
} yield number
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用嵌套的flatMaps