Say I have a function that can take an optional parameter, and I want to return a Some
if the argument is None
and a None
if the argument is Some
:
def foo(a: Option[A]): Option[B] = a match {
case Some(_) => None
case None => Some(makeB())
}
So what I want to do is kind of the inverse of map
. The variants of orElse
are not applicable, because they retain the value of a
if it's present.
Is there a more concise way to do this than if (a.isDefined) None else Some(makeB())
?
Overview of this answer:
fold
fold
fold
-solution could be just as "obvious" as the if-else
-solution.Solution
You can always use fold
to transform Option[A]
into whatever you want:
a.fold(Option(makeB())){_ => Option.empty[B]}
Demo
Here is a complete runnable example with all the necessary type definitions:
class A
class B
def makeB(): B = new B
def foo(a: Option[A]): Option[B] = a match {
case Some(_) => None
case None => Some(makeB())
}
def foo2(a: Option[A]): Option[B] =
a.fold(Option(makeB())){_ => Option.empty[B]}
println(foo(Some(new A)))
println(foo(None))
println(foo2(Some(new A)))
println(foo2(None))
This outputs:
None
Some(Main$$anon$1$B@5fdef03a)
None
Some(Main$$anon$1$B@48cf768c)
Why fold
only seems less intuitive
In the comments, @TheArchetypalPaul has commented that fold
seems "lot less obvious" than the if-else
solution. I agree, but I still think that it might be interesting to reflect on the reasons why that is.
I think that this is mostly an artifact resulting from the presence of special if-else
syntax for booleans.
If there were something like a standard
def ifNone[A, B](opt: Option[A])(e: => B) = new {
def otherwise[C >: B](f: A => C): C = opt.fold((e: C))(f)
}
syntax that can be used like this:
val optStr: Option[String] = Some("hello")
val reversed = ifNone(optStr) {
Some("makeB")
} otherwise {
str => None
}
and, more importantly, if this syntax was mentioned on the first page of every introduction to every programming language invented in the past half-century, then the ifNone-otherwise
solution (that is, fold
), would look much more natural to most people.
Indeed, the Option.fold
method is the eliminator of the Option[T]
type: whenever we have an Option[T]
and want to get an A
out of it, the most obvious thing to expect should be a fold(a)(b)
with a: A
and b: T => A
. In contrast to the special treatment of booleans with the if-else
-syntax (which is a mere convention), the fold
method is very fundamental, the fact that it must be there can be derived from the first principles.