Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Short-circuiting function from foreach

My colleagues and I are a bit stumped over the following code's behavior.

def a: String = {
  None.foreach(return "1")
  return "2"
}

def b: String = {
  None.foreach(x => return "1")
  return "2"
}

As expected, invoking b does return "2". However, invoking a returns "1". When exactly is return "1" being evaluated when a is executed?

like image 261
Nick Avatar asked Nov 30 '12 21:11

Nick


2 Answers

All* function evaluations of the form

f({code})

are equivalent to

val temp = { code }
f(temp)

So, in the first case,

val temp = return "1"
None.foreach(temp)   // Never reach this point!

While in the second,

val temp = (x: Nothing) => return 1
  // Equivalent: new Function1[Nothing,String]{ def apply(x: Nothing) = return "1" }
None.foreach(temp)   // Never call that weird function!

so everything is okay.

But, wait, foreach takes an A => Unit. How is return "1" such a function? Well, Scala starts with the most specific type possible (Nothing, which is a subclass of anything, and therefore promises to do anything you ask of it, except it can't exist). And, then, since no values is produced by the statement (control escapes via a return), it never modifies it from Nothing. So, indeed, Nothing is a subclass of Function1[A,Unit].

And to produce that Nothing--well, to pretend to produce it--you actually run the code, and return.

* Actually, if the parameter is passed by name, it's secretly converted to () => { Code } and passed in without evaluation.

like image 132
Rex Kerr Avatar answered Oct 04 '22 00:10

Rex Kerr


The others have explained the behaviour, however it is best not to use return at all.

I would write something like.

def b: String = {
  None.map(x => "1").getOrElse("2")
}

Or if it was a List I would use collectFirst if I wanted to return the first item that matched something.

EDIT: I see the question is tagged as functional programming. You should avoid return if trying to program in a functional style. Take map which is A => B. If you use return you voilate that type signature.

def a: String = { 
  val a = Option(5).map(x => if(x==6) return "aa" else 6); // isnt returning a `B` when x == 5.
  a.toString
};
like image 22
Ivan Meredith Avatar answered Oct 03 '22 23:10

Ivan Meredith