为什么Option没有折叠方法?

时间:2011-03-16 15:57:42

标签: scala map fold scala-option

我想知道为什么scala.Option没有这样定义的方法fold

fold(ifSome: A => B , ifNone: => B)

相当于

map(ifSome).getOrElse(ifNone)

没有比使用map + getOrElse更好的了吗?

4 个答案:

答案 0 :(得分:67)

我个人发现像cata这样的方法需要两个闭包,因为参数往往过度。你真的在map + getOrElse上获得了可读性吗?想想你的代码的新手:他们会做什么

opt cata { x => x + 1, 0 }

你真的认为这比

更清楚
opt map { x => x + 1 } getOrElse 0

事实上,我认为两者都不比优秀的旧

更好
opt match {
  case Some(x) => x + 1
  case None => 0
}

与往常一样,有一个限制,即额外的抽象不会给你带来好处并且会产生适得其反的效果。

答案 1 :(得分:41)

最后添加了in Scala 2.10,签名为fold[B](ifEmpty: => B)(f: A => B): B

不幸的是,这有一个共同的负面后果:B仅针对基于ifEmpty参数的调用推断,实际上这通常更为狭窄。例如。 (正确的版本已经在标准库中,这仅用于演示)

 def toList[A](x: Option[A]) = x.fold(Nil)(_ :: Nil)

Scala会将B推断为Nil.type而不是期望的List[A],并抱怨f没有返回Nil.type。相反,你需要一个

 x.fold[List[A]](Nil)(_ :: Nil)
 x.fold(Nil: List[A])(_ :: Nil)

这使fold与相应的match不完全相同。

答案 2 :(得分:25)

你可以这样做:

opt foldLeft (els) ((x, y) => fun(x))

(els /: opt) ((x,y) => fun(x))

(两个解决方案都会按值评估 els ,这可能不是您想要的。感谢Rex Kerr指向它。)

修改

但你真正想要的是Scalaz的catamorphism cata(基本上是fold,它不仅处理Some值,还会映射None部分,这就是你所描述的)

opt.cata(fun, els)

定义为(其中value是拉皮条选项值)

def cata[X](some: A => X, none: => X): X = value match {
  case None => none
  case Some(a) => some(a)
}

相当于opt.map(some).getOrElse(none)

虽然我应该说,只有在表达它的“更自然”的方式时才应该使用cata。在很多情况下,简单的map - getOrElse就足够了,特别是当它涉及潜在链接大量map时。 (虽然你也可以用函数组合来链接fun,当然 - 这取决于你是否要关注函数组合或值转换。)

答案 3 :(得分:19)

如Debilski所述,您可以使用Scalaz的OptionW.catafold。正如Jason评论的那样,命名参数使它看起来很漂亮:

opt.fold { ifSome = _ + 1, ifNone = 0 }

现在,如果某个None mzero案例中您想要的值为Monoid[M],而f: A => M案例的Some函数为opt foldMap f ,你可以这样做:

opt map (_ + 1) getOrElse 0

所以,

opt foldMap (_ + 1)

变为

Option

就我个人而言,我认为apply应该有一个opt { _ + 1, 0 } 方法,这将是一种变形。这样你就可以这样做:

opt { some = _ + 1, none = 0 }

{{1}}

事实上,对于所有代数数据结构来说,这将是很好的。