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.
fold is more concise than pattern matching
val op:Option[B] = ...
val inv = op.fold(Option(makeB()))(_ => None)
I've come up with this definition a.map(_ => None).getOrElse(Some(makeB()))
:
scala> def f[A](a: Option[A]) = a.map(_ => None).getOrElse(Some(makeB()))
f: [A](a: Option[A])Option[makeB]
scala> f(Some(44))
res104: Option[makeB] = None
scala> f(None)
res105: Option[makeB] = Some(makeB())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With