开发者

Why doesn't Option have a fold method?

开发者 https://www.devze.com 2023-02-17 09:37 出处:网络
I wonder wh开发者_StackOverflow社区y scala.Option doesn\'t have a method fold like this defined:

I wonder wh开发者_StackOverflow社区y 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?


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.


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.


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.)


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号