Idiomatic Scala for options instead of if / else / else chain

I often write Scala forms:

def foo = { f1() match { case Some(x1) => x1 case _ => f2() match { case Some(x2) => x2 case _ => f3() match { case Some(x3) => x3 case _ => f4() } } } } 

This is the moral equivalent of Java

 Object foo() { Object result = f1(); if (result != null) { return result; } else { result = f2(); if (result != null) { return result; } else { result = f3(); if (result != null) { return result; } else { return f4(); } } } } 

and seems ugly and overly detailed. I feel that there should be a readable way to do this on one line of Scala, but I don’t understand what it is.

Note. I watched Idiomatic Scala for nested parameters , but this is a slightly different matter.

+5
source share
4 answers

I know I'm late for the party, but I feel that the orElse solution is a bit awkward here. For me, a common functional idiom (not just a scalaic) would be sth. along these lines (forgive me, I'm not a scala experienced):

 def f1 = () => { println("f1 here"); null } def f2 = () => { println("f2 here"); null } def f3 = () => { println("f3 here"); 2 } def f4 = () => { println("f4 here"); 3 } def f5 = () => { println("f5 here"); 43 } Stream(f1, f2, f3, f4, f5) .map(f => f()) .dropWhile(_ == null) .head 

You use Stream as a lazy list, and basically you say: start calling functions and give me the first one, which will not be zero. Combining the declarative approach and laziness gives you this common piece of code, in which the only thing you need to change when changing the number of functions is the input list (stream), adding another functional element. Thus, the functions f1 ... fn become data, so you do not need to modify any existing code.

+7
source

The idiomatic way of writing nested pattern matching with parameters in Scala is done using the map , flatMap , orElse and getOrElse .

You use map if you want to continue processing the contents of the option and save the optional behavior:

So instead:

 val opt: Option[Int] = ??? opt match { case Some(x) => Some(x + 1) case None => None } 

You would do this:

 val opt: Option[Int] = ??? opt.map(_ + 1) 

This chaining is much more natural:

 opt.map(_ + 1).map(_ * 3).map(_ - 2) 

flatMap pretty similar, but is used when your further operations also return a parameter type.

In your specific example, orElse seems to be the most tailored solution. You can use orElse to return the option itself if it is not empty, or return an argument. Note that the argument is lazily evaluated, so it is really equivalent to the nested pattern matching / if -then-else.

 def foo = { f1().orElse(f2()) .orElse(f3()) .orElse(f4()) } 

You can also combine them with getOrElse if you want to get rid of the option. In your example, you seem to be returning the final f4 , as if it were not returning Option , so you would do the following:

 def foo = { f1().orElse(f2()) .orElse(f3()) .getOrElse(f4()) } 
+7
source

You may try:

 f1() orElse f2() orElse Option(f3()) getOrElse f4() 

Assuming f1 and f2 return parameters of the same type, and f3 and f4 return non-parameters of the same type

EDIT

This is not entirely clear from your example, if f3() returns Option or not, so if this happens, then the code will be simplified:

 f1() orElse f2() orElse f3() getOrElse f4() 
+4
source

A little tweak to cmbaxter's answer, which saves a few characters, but otherwise the same.

 def foo = f1 orElse f2 orElse f3 getOrElse f4 
0
source

All Articles