【问题标题】:Why doesn't Option have a fold method?为什么 Option 没有 fold 方法?
【发布时间】:2011-07-16 17:27:24
【问题描述】:

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

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

相当于

map(ifSome).getOrElse(ifNone)

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

【问题讨论】:

  • 你真的想要else: => B——实际上它不应该被称为else,因为这是一个保留字。但无论如何,您希望通过名称传递 option-is-empty 案例,以便获得惰性评估。
  • 已编辑以将类型签名修复到规范折叠。
  • 我远不是一个 scalaz 策划者——实际上是一个 scala 初学者——但我实际上更喜欢 map 和 getOrElse。我发现fold 只是要学习的其他东西。也许我需要更新我的思想。
  • 另见stackoverflow.com/questions/5654004/…,它显示(接近尾声)选项的 ifSome/ifNone 皮条客。

标签: scala map fold scala-option


【解决方案1】:

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

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

【讨论】:

  • 根据编辑,此特定调用只有一个函数参数。命名参数提供了另一种使事物可读的方法:opt.fold(ifSome = _ + 1, ifNone = 0)。如果函数定义得更早,这使得它可以在其他地方使用,我觉得这特别好:opt.fold(f, 0).
  • OptionW.cata 更易读的别名是 e.g. opt some { _ + 1 } none { 0 }。 this 和 foldcata 还具有强制 s 和 n 返回相同类型的好处,而您可能会得到一个意想不到的类型,例如(None: Option[Int]) getOrElse ("2") // returns an Any。由Learn You a Scalaz指出。
  • 对我来说问题是 IntelliJ 将 map-getOrElse 模式标记为问题并建议改用 fold。
【解决方案2】:

终于添加了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 将推断BNil.type 而不是所需的List[A] 并抱怨f 没有返回Nil.type。相反,您需要其中之一

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

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

【讨论】:

    【解决方案3】:

    你可以这样做:

    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 是“更自然”的表达方式时才使用它。在很多情况下,一个简单的mapgetOrElse 就足够了,尤其是当它涉及到可能链接大量maps 时。 (当然,您也可以将funs 与函数组合链接起来——这取决于您是要关注函数组合还是值转换。)

    【讨论】:

    • 值得指出的是,这使用了 Option -> 可迭代隐式转换,并且 els 参数不是按名称命名的,因此它每次都会进行评估。
    • 这在原始规范中没有提到。
    • 同意——没有提到。但对于大多数这样的控制结构来说,这是一件好事。
    • opt.foldLeft(els)((_,elem) => fun(elem)) 实际上你的第一个例子应该怎么读,@Debilski?我这样说是因为您想将foo 应用于foldLeft(opt)的调用者而不是elsgist.github.com/kman007us/8485124这是真的吗? (此外,您是否发现使用_ 而不是y 来表示未使用的参数更清晰?)
    • @KevinMeredith 没错,累加器是第一个参数,而不是第二个,所以fun 应该应用于第二个。
    【解决方案4】:

    正如 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 方法,这将是catamorphism。这样你就可以这样做:

    opt { _ + 1, 0 }
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-09
      • 2014-07-06
      • 1970-01-01
      • 1970-01-01
      • 2015-05-01
      • 2014-03-07
      • 2011-06-12
      相关资源
      最近更新 更多