How does Scala implement returning from within an expression?

For example, if we have a method such as

def find[A](xs: Seq[A], p: A => Boolean): Option[A] = { xs.foreach(x => if (p(x)) return Some(x)); None; } 

(of course, there is a library function for this, this is just an example). How is foreach executed when executing the return s internal function?

Or in

 def foo(x: AnyRef): String = process(x match { case (s: String) => s; case _ => return ""; }) 

how to avoid process execution when return "" executed?

+6
source share
1 answer

foo example depends on which of

 def process(s: String): String def process(s: => String): String 

this is. I guess the first one, since you suggest the process not to run. This is always the same as always when you pass an argument β€” first do the work of creating the arguments, and then you call the method. Since you are faced with return , this is easy: you just call the corresponding return from the bytecode when creating the * argument and never include the method call. Thus, this is just a local income.

The find example is a bit more related. Try the simplest example based on foo , which requires a non-local return:

 class Nonlocal { def pr(s: => String) = { println(s); "Printed" } def foo(x: AnyRef): String = pr(x match { case (s: String) => s; case _ => return ""; }) } 

foo body is equivalent

 import scala.runtime.NonLocalReturnControl val temp = new AnyRef try { pr(x match { case s: String => s case _ => throw new NonLocalReturnControl(temp, "") }) } catch { case nlrc: NonLocalReturnControl[_] if (nlrc.key eq temp) => nlrc.value.asInstanceOf[String] } 

The key factors that should be noted are that the sentinel is created in such a way that these things can be arbitrarily nested without interfering with each other, and that NonLocalReturnControl returns the correct value. Unsurprisingly, this is not entirely cheap compared to returning, say, Int . But since it throws an exception without a stack trace (safe because it cannot escape: the catch guaranteed to catch it), this is not so bad - it is as bad as calling a trigger function or summing an array with dozens of records.

Also note that pr is only partially executed before it gets it. In this case, it does not print anything, because the first thing it does is try to use s to fill the actual line, but then it gets into the exception, which returns control back to foo . (This way you get an empty string from foo , but don't print anything.)

* In fact, in bytecode, it tends to be a jump to the end of the method, with loading / returning there. However, conceptually.

+3
source

Source: https://habr.com/ru/post/949658/


All Articles