36

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

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

相当于

map(ifSome).getOrElse(ifNone)

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

4

4 回答 4

74

我个人发现像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
}

与往常一样,额外的抽象不会给您带来好处并会适得其反。

于 2011-03-16T22:31:34.937 回答
41

它最终被添加到 Scala 2.10中,并带有签名fold[B](ifEmpty: => B)(f: A => B): B

不幸的是,这有一个常见的负面后果:B仅根据ifEmpty论点推断调用,这在实践中通常更狭窄。例如(标准库中已经有一个正确的版本,这只是为了演示)

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

Scala 将推断BNil.type而不是期望List[A]并抱怨f不返回Nil.type。相反,您需要其中之一

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

这使得fold不完全等同于对应match的 .

于 2013-02-02T14:22:22.620 回答
25

你可以做:

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

或者

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

(两种解决方案都将按 els 价值进行评估,这可能不是您想要的。感谢Rex Kerr指出它。)

编辑:

但是你真正想要的是 Scalaz 的catamorphism cata(基本上 afold不仅处理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–<code>getOrElse 就足够了,尤其是当它涉及到潜在地链接大量maps 时。(当然,您也可以将funs 与函数组合链接起来——这取决于您是要关注函数组合还是值转换。)

于 2011-03-16T16:18:07.660 回答
19

正如 Debilski 所提到的,您可以使用 ScalazOptionW.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方法,那就是catamorphism。这样你就可以这样做:

opt { _ + 1, 0 }

或者

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

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

于 2011-03-16T23:05:09.993 回答