35

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?

giampaolo
  • 6,906
  • 5
  • 45
  • 73
soc
  • 27,983
  • 20
  • 111
  • 215
  • 2
    You really want `else: => B`--and actually it shouldn't be called `else` since that's a reserved word. But anyway, you want the option-is-empty case to be passed by name so you get lazy evaluation. – Rex Kerr Mar 16 '11 at 16:56
  • Edited to fix the type signatures to the canonical fold. – Rex Kerr Mar 16 '11 at 18:21
  • 3
    I'm far from a scalaz mastermind -- actually a scala beginner --, but I actually prefer map and getOrElse. I find `fold` to be just something else to to learn. Maybe I need my mind renewed. – Bradford Mar 16 '11 at 20:48
  • See also http://stackoverflow.com/questions/5654004/implementing-iftrue-iffalse-ifsome-ifnone-etc-in-scala-to-avoid-if-and which shows (near the end) an ifSome/ifNone pimp for Option. – Jean-Philippe Pellet Apr 16 '11 at 10:42

4 Answers4

75

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.

Apocalisp
  • 34,834
  • 8
  • 106
  • 155
Martin Odersky
  • 20,470
  • 9
  • 51
  • 49
  • 1
    As per the edit, there's only one function parameter for this particular call. Named arguments provides another way to make things readable: `opt.fold(ifSome = _ + 1, ifNone = 0)`. If the function is defined earlier, which makes it usable elsewhere, I find this particularly nice: `opt.fold(f, 0)`. – retronym Mar 16 '11 at 23:15
  • 2
    A more readable alias for OptionW.cata is e.g. `opt some { _ + 1 } none { 0 }`. This and `fold` and `cata` also have the benefit of enforcing s and n return the same type, whereas you might get an unintended type with e.g. `(None: Option[Int]) getOrElse ("2") // returns an Any`. Pointed out by [Learn You a Scalaz](https://github.com/jrwest/learn_you_a_scalaz/blob/master/1-intro.md). – Bluu Nov 14 '11 at 03:10
  • 2
    The problem for me is that IntelliJ marks the map-getOrElse pattern as an issue and suggests using fold instead. – Chris Beach Aug 18 '14 at 13:59
41

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.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
24

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

Community
  • 1
  • 1
Debilski
  • 66,976
  • 12
  • 110
  • 133
  • 5
    It's worth pointing out that this uses Option -> Iterable implicit conversion and the `els` argument isn't by-name so it evaluates every time. – Rex Kerr Mar 16 '11 at 16:55
  • This has not been mentioned in the original specification. – Debilski Mar 16 '11 at 19:39
  • 1
    Agreed--it wasn't mentioned. But it's a good thing to have for most any control structure like this. – Rex Kerr Mar 16 '11 at 19:53
  • Isn't `opt.foldLeft(els)((_,elem) => fun(elem))` actually how your first example should read, @Debilski? I say this since you want to apply `foo` to the caller of `foldLeft` (opt) rather than `els`. https://gist.github.com/kman007us/8485124 Is that true? (Additionally, do you find it clearer to use `_` rather than `y` for an un-used argument?) – Kevin Meredith Jan 18 '14 at 01:56
  • @KevinMeredith True, the accumulator is the first parameter, not the second, so `fun` should be applied over the second one. – Daniel C. Sobral Jan 30 '14 at 18:17
  • This answer seems redundant now and should be removed – Chris Beach Aug 18 '14 at 14:00
19

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.

Apocalisp
  • 34,834
  • 8
  • 106
  • 155