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.