为什么 Option 没有折叠方法?

发布于 2024-10-22 07:12:50 字数 275 浏览 2 评论 0原文

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

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

相当于

map(ifSome).getOrElse(ifNone)

是否没有比使用 map + getOrElse

I wonder why scala.Option doesn't have a method fold like this defined:

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

equivalent to

map(ifSome).getOrElse(ifNone)

Is there no better than using map + getOrElse?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

假面具 2024-10-29 07:12:50

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

一如既往,存在一个限制,额外的抽象不会给你带来好处和转变适得其反。

I personally find methods like cata that take two closures as arguments are often overdoing it. Do you really gain in readability over map + getOrElse? Think of a newcomer to your code: What will they make of

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

Do you really think this is clearer than

opt map { x => x + 1 } getOrElse 0

In fact I would argue that neither is preferable over the good old

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

As always, there's a limit where additional abstraction does not give you benefits and turns counter-productive.

抽个烟儿 2024-10-29 07:12:50

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

不幸的是,这会带来一个常见的负面后果:仅根据 ifEmpty 参数推断出 B 调用,而实际上,该参数的范围通常更窄。例如(标准库中已经有正确的版本,这仅用于演示)

 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

It was finally added in Scala 2.10, with the signature fold[B](ifEmpty: => B)(f: A => B): B.

Unfortunately, this has a common negative consequence: B is inferred for calls based only on the ifEmpty argument, which is in practice often more narrow. E.g. (a correct version is already in the standard library, this is just for demonstration)

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

Scala will infer B to be Nil.type instead of desired List[A] and complain about f not returning Nil.type. Instead, you need one of

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

This makes fold not quite equivalent to corresponding match.

一场信仰旅途 2024-10-29 07:12:50

您可以执行以下操作:

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 是 pimped 选项值),

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与函数组合链接起来——这取决于您是否想专注于函数组合或值转换。)

You can do:

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

or

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

(Both solutions will evaluate els by value, which might not be what you want. Thanks to Rex Kerr for pointing at it.)

Edit:

But what you really want is Scalaz’s catamorphism cata (basically a fold which not only handles the Some value but also maps the None part, which is what you described)

opt.cata(fun, els)

defined as (where value is the pimped option value)

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

which is equivalent to opt.map(some).getOrElse(none).

Although I should remark that you should only use cata when it is the ‘more natural’ way of expressing it. There are many cases where a simple mapgetOrElse suffices, especially when it involves potentially chaining lots of maps. (Though you could also chain the funs with function composition, of course – it depends on whether you want to focus on the function composition or the value transformation.)

你怎么这么可爱啊 2024-10-29 07:12:50

正如 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 }

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

As mentioned by Debilski, you can use Scalaz's OptionW.cata or fold. As Jason commented, named parameters make this look nice:

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

Now, if the value you want in the None case is mzero for some Monoid[M] and you have a function f: A => M for the Some case, you can do this:

opt foldMap f

So,

opt map (_ + 1) getOrElse 0

becomes

opt foldMap (_ + 1)

Personally, I think Option should have an apply method which would be the catamorphism. That way you could just do this:

opt { _ + 1, 0 }

or

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

In fact, this would be nice to have for all algebraic data structures.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文