I’d like to suggest syntax sugar like below.
val maybeA = None
val maybeB = None
val maybeC = Some(3)
val x: Int = getOrElse {
a <- maybeA
b <- maybeB
c <- maybeC
} yield 0
// x == 3
translated into
val maybeA = None
val maybeB = None
val maybeC = Some(3)
val x = maybeA orElse maybeB orElse maybeC getOrElse 0
// x == 3
curoli
April 8, 2020, 1:24pm
2
It appears you cannot use the variables a
, b
and c
, because evaluation only continues when they are undefined. Why even name them?
Why not define some method like:
getFirst(maybeA, maybeB, maybeC)(0)
5 Likes
Given that, this seems to work.
def getFirst[A](as: Option[A]*)(default: A): A = {
as.reduceLeft((o1, o2) => if (o1.nonEmpty) o1 else o2).getOrElse(default)
}
I dislike the if
, but it was easy to write and nothing else occurred to me quickly. This seems simple enough that it doesn’t need syntax to clutter up the language.
3 Likes
Why even name them?
I didn’t think of it until you said. They are unnecessary.
Or simply
def getFirst[A](as: Option[A]*)(default: A): A = {
as.reduceLeft(_ orElse _).getOrElse(default)
}
4 Likes
Please let me explain further about this topic.
At first, I suggested that sugar because I want to avoid below:
writing long expression as orElse argument
writing long orElse
method chain
But it seems unnecessary by using getFirst
.
That’s the function I wanted!
Or:
def getFirst[A](as: Option[A]*)(default: A): A =
as.find(_.isDefined).getOrElse(default)
That even works for empty as
.
2 Likes
tarsa
April 8, 2020, 6:22pm
9
That needs extra .flatten
. Below code works:
def getFirst[A](as: Option[A]*)(default: A): A =
as.find(_.isDefined).flatten.getOrElse(default)
I have even shorter alternative:
def getFirst[A](as: Option[A]*)(default: A): A =
as.foldRight(default)(_ getOrElse _)
2 Likes
curoli
April 8, 2020, 6:43pm
10
How about adding ||
to Option as a synonym for orElse? So you could write it as:
maybeA || maybeB || maybeC || Some(3)
And how about a &&
method such that
Some(a) && Some(b) == Some((a, b))
Some(a) && Some(b) && Some(c) == Some((a,b,c))
and None
if any operand is None
?
This is zip
This isn’t compatible with the previous snippet, because of associativity. It would have to be Some(((a, b), c))
(which is what zip
would give you).
2 Likes
Katrix
April 8, 2020, 7:16pm
12
You’d need implicit machinery for the &&
case to not just get nested tuples. Also in general I’m iffy on adding more symbolic operators here.
LPTK
April 8, 2020, 7:22pm
13
Let me add my own stone to the code-golfing edifice: the default value should probably be by-name, to be consistent with the usual semantics:
def getFirst[A](as: Option[A]*)(default: => A): A =
as.collectFirst{ case Some(v) => v }.getOrElse(default)
6 Likes
I really don’t think the suggested “getOrElse-comprehension” is less verbose than just chaining orElse, or suggestions above.
3 Likes