What is the difference between `Option.fold()()` and `Option.map().getOrElse()`?

Option.fold is more safer than .getOrElse. you can see the definition for .fold below where both ifEmpty and f are of type B. (introduced only after scala 2.10 probably)

  @inline final def fold[B](ifEmpty: => B)(f: A => B): B =
    if (isEmpty) ifEmpty else f(this.get)

which means you will probably not mess up the data types (exception below),

scala> val data = Option("massive data").fold(-1) { _ => 1 }
data: Int = 1

// but if i try to return different type in either of ifEmpty or f
// compiler will curse me right at my face   
scala> val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
<console>:17: error: type mismatch;
 found   : String("Let me caught by compiler")
 required: Int
       val data = Option("massive data").fold(-1) { _ => "Let me caught by compiler" }
                                                     ^

While getOrElse is not as safer unless you provide the type (supertype B in following definition) manually.

  @inline final def getOrElse[B >: A](default: => B): B =
    if (isEmpty) default else this.get

which means you can return a different type from getOrElse than what the original value wrapped in Option[A] was.

scala> val data = Option("massive data").map(_ => 1).getOrElse(List("I'm not integer"))
data: Any = 1

//you have to manually mention the type to getOrElse to restrict, 
// which is not that smart in my opinion
scala> val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
<console>:17: error: type mismatch;
 found   : List[String]
 required: Int
       val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
                                                                    ^

Interesting thing is you can return unit from getOrElse or fold which can introduce bugs in an application unless you catch in unit tastes.

scala> val data = Option("massive data").fold() { _ => 1 }
data: Unit = ()

scala> val data = Option("massive data").map(_ => 1).getOrElse()
data: AnyVal = 1

As a counterpoint to @prayagupd's answer, fold often invites you to mess up types in a specific way.

The problem is that by Scala's rules, only ifEmpty is used to infer B and then f is checked to be suitable. Which means that using None or Nil as ifEmpty, which is quite common, will lead to their singleton types being used as B instead of Option/List[SomeType], no matter what f returns.

Of course, there are workarounds: specify B explicitly, use Option.empty[SomeType] or None: Option[SomeType] instead of None. Or just use pattern-matching.

Tags:

Scala