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