Nick
Nick

Reputation: 572

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?

Upvotes: 10

Views: 819

Answers (5)

user451816
user451816

Reputation:

Scala uses eager, applicative-order evaluation. This means that an argument is evaluated before it is substituted for the parameters of the function. Also, it is worth recalling that functions evaluate to themselves. It is function application that evaluates to the returned value of the function. Now let's look at what each of these arguments evaluate to.

scala> :t () => "1"
() => java.lang.String

So, in the second case, we get a function. It is being evaluated (to itself) before it is passed, but never applied, because None has no elements. However, return "1" doesn't need to be applied. It just needs to be evaluated, which has the side-effect of redirecting control, and then returning "1" from the function containing it.

Upvotes: 1

Ivan Meredith
Ivan Meredith

Reputation: 2222

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
};

Upvotes: 2

Rex Kerr
Rex Kerr

Reputation: 167921

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.

Upvotes: 11

grauwulf
grauwulf

Reputation: 376

in a, the foreach executes return "1" before it gets to the return "2" line. Even thought it's in a foreach the code still executes and so it returns 1. in b, x is being assigned through a 'self type annotation' ( http://www.scala-lang.org/node/124 ) to the operation of return "1"

This post on scala inheritance may be of use: Using this keyword to inherit?

Share and enjoy

Upvotes: 1

Chris Martin
Chris Martin

Reputation: 30756

That's really cool - I didn't know that. Looking at the language reference, page 91 of http://www.scala-lang.org/docu/files/ScalaReference.pdf :

A return expression return e must occur inside the body of some enclosing named method or function. The innermost enclosing named method or function in a source program, f , must have an explicitly declared result type, and the type of e must conform to it. The return expression evaluates the expression e and returns its value as the result of f.

...

An apply method which is generated by the compiler as an expansion of an anonymous function does not count as a named function in the source program, and therefore is never the target of a return expression.

Returning from a nested anonymous function is implemented by throwing and catching a scala.runtime.NonLocalReturnException. Any exception catches between the point of return and the enclosing methods might see the exception. A key comparison makes sure that these exceptions are only caught by the method instance which is terminated by the return.

If the return expression is itself part of an anonymous function, it is possible that the enclosing instance of f has already returned before the return expression is executed. In that case, the thrown scala.runtime.NonLocalReturnException will not be caught, and will propagate up the call stack

Upvotes: 1

Related Questions