Escaping from search

This one also throws an exception and it doesn’t use return. I don’t really understand why.

BTW what is the meaning of the error? java.lang.ExceptionInInitializerError

But returning/throwReturn is still brittle, right?, as the throwReturn may not be lexically within the returning. It doesn’t seem to provide a way for the programmer to return from any of several concentric returning calls. — a problem which continuations are intended to solve.

You’ve just emulated non-local returns. The mechanics and problem are the same. process returns before elements were mapped (because LazyList is lazy, i.e. element are computed on first usage) and actual mapping happens elsewhere, in this case during .mkString.

returning/throwReturn is exactly what non-local returns are today, just written explicitly. I’ll show a disassembly soon to show what’s going on.

I’m sorry I don’t know what this means. What are non-local returns today? Are you talking about return or about throw/catch?

Source code in Scala:

class Demo {
  def withLocalReturn(nameOpt: Option[String]): String = {
    nameOpt match {
      case Some(name) => return s"Hello, $name!"
      case None => "Hello!"

  def withNonLocalReturn(nameOpt: Option[String]): String = { { name =>
      return s"Hello, $name!"

After compilation to *.class file and decompilation to *.java code IntelliJ shows that:

//decompiled from Demo.class
import java.lang.invoke.SerializedLambda;
import scala.MatchError;
import scala.Option;
import scala.Some;
import scala.None.;
import scala.reflect.ScalaSignature;
import scala.runtime.NonLocalReturnControl;

   bytes = "\u0006\u0005!2A\u0001B\u0003\u0001\u0011!)q\u0002\u0001C\u0001!!)1\u0003\u0001C\u0001)!)Q\u0005\u0001C\u0001M\t!A)Z7p\u0015\u00051\u0011a\u0002\u001ff[B$\u0018PP\u0002\u0001'\t\u0001\u0011\u0002\u0005\u0002\u000b\u001b5\t1BC\u0001\r\u0003\u0015\u00198-\u00197b\u0013\tq1B\u0001\u0004B]f\u0014VMZ\u0001\u0007y%t\u0017\u000e\u001e \u0015\u0003E\u0001\"A\u0005\u0001\u000e\u0003\u0015\tqb^5uQ2{7-\u00197SKR,(O\u001c\u000b\u0003+\u0001\u0002\"AF\u000f\u000f\u0005]Y\u0002C\u0001\r\f\u001b\u0005I\"B\u0001\u000e\b\u0003\u0019a$o\\8u}%\u0011AdC\u0001\u0007!J,G-\u001a4\n\u0005yy\"AB*ue&twM\u0003\u0002\u001d\u0017!)\u0011E\u0001a\u0001E\u00059a.Y7f\u001fB$\bc\u0001\u0006$+%\u0011Ae\u0003\u0002\u0007\u001fB$\u0018n\u001c8\u0002%]LG\u000f\u001b(p]2{7-\u00197SKR,(O\u001c\u000b\u0003+\u001dBQ!I\u0002A\u0002\t\u0002"
public class Demo {
   public String withLocalReturn(final Option nameOpt) {
      if (nameOpt instanceof Some) {
         Some var4 = (Some)nameOpt;
         String name = (String)var4.value();
         return (new StringBuilder(8)).append("Hello, ").append(name).append("!").toString();
      } else if (.MODULE$.equals(nameOpt)) {
         String var2 = "Hello!";
         return var2;
      } else {
         throw new MatchError(nameOpt);

   public String withNonLocalReturn(final Option nameOpt) {
      Object var2 = new Object();

      String var10000;
      try {
         var10000 = (String) -> {
            throw new NonLocalReturnControl(var2, (new StringBuilder(8)).append("Hello, ").append(name).append("!").toString());
         }).getOrElse(() -> {
            return "Hello!";
      } catch (NonLocalReturnControl var4) {
         if (var4.key() != var2) {
            throw var4;

         var10000 = (String)var4.value();

      return var10000;

   // $FF: synthetic method
   private static Object $deserializeLambda$(SerializedLambda var0) {
      return Class.lambdaDeserialize<invokedynamic>(var0);

As you see, the withNonLocalReturn contains try/catch with throw inside a lambda. If you move that lambda elsewhere then returning exception won’t be caught by that try/catch mechanism as it only works when the exception is thown during execution of that try/catch block.

This example is indeed enlightening. I admit, I don’t understand why it doesn’t work.
In my code, when the instance of the NonLocalExit class is constructed, what is the value of body which initializes the ident slot of the instance? Why doesn’t the catch catch it?

Perhaps in light of your recent post, perhaps the problem is that the exception gets through outside the dynamic extent of the catch ???

Here’s even simpler example:

object Demo {
  def main(args: Array[String]): Unit = {
    val fn = {
      try { // try catches exceptions thrown during its evaluation
        // but here we're returning a function without evaluating it
        () => throw new Exception("my exception")
      } catch {
        case e: Exception =>
          println(s"Exception caught: $e")
          () => ()
    // here we run the function with a `throw` inside
    // but we aren't in a try/catch block, so we get unhandled exception

If you look closely then you’ll see that it’s exactly the same mechanism as visible in the decompiled code in my post above.

I don’t understand. You return a function which when called will throw an exception within a try/catch. Right? Or am I misreading it?

Edited: Oh I see, the function returned is not the { try throw} rather it is simply the () => throw.... yes so when it is called the exception is thrown outside a try.

Yes. Or maybe yet another explanation. Here’s how non-local returns are desugared to local returns according to the decompiled code:

Original code:

def method(as: Seq[String]): String = {
  as.foreach { a =>
    if (a.size == 3) return a // non-local return

After desugaring:

def method(as: Seq[String]): String = {
  val marker = ???
  try {
    as.foreach { a =>
      if (a.size == 3) throw new NonLocalReturnControl(marker, a)
  } catch {
    case e: NonLocalReturnControl if e.key == marker =>
      return e.value // local return

So do I understand correctly now that the reason this doesnt work, is because when seq is a lazy list, then doesn’t iterate, but rather returns a function which will iterate later. And at that later point in time, the dynamic extent of block has finished.

It’s related to how scastie works. It wraps a worksheet into an object and moves the initialization code to static initilalizer. When that fails the aforementioned exception is thrown by JVM. doesn’t run fn over lazyList elements until it’s forced. That’s the point of lazyList. Example:

LazyList(1, 2, 3).map(_ => sys.error("boom")) // doesn't throw, will throw when you run e.g. .mkString
Seq(1, 2, 3).map(_ => sys.error("boom")) // throws immediately

yes, makes much more sense now. Thanks for the explanation.

Above, I mentioned a function I’d like to have called cousinOfFind. Does that concept suffer from the same problem as non-local exits?

I think writing such a function would be pretty difficult, as I can’t write a function which takes any argument for which find can be called on.

The following works (I think) if the input is of type Seq, but doesn’t work on Set for example. And what about some input such as an iterator which I cannot read the same value from multiple times.

val data = Seq(1,2,3,2,3,4,3,4,5,4,5,6)

def f(x:Int):Int = {
  println(s"x=$x  y=${2*x+3}")
def cousinOfFind[A,B](seq:Seq[A],f:A=>B,pred:B=>Boolean):Option[(A,B)] = {
  val v = seq.view{case (_,y) => pred(y)}

       (x => x == 11):Int=>Boolean)

cousinOfFind seems strict and synchronous because it returns a tuple (which is a strict and synchronous data structure) of ordinary values. Therefore non-local returns should work inside it if you use them in strict and synchronous transformations. Let’s say you can do:

def cousinOfFind[A, B](seq:Findable[A], f:A => B, predicate: B => Boolean): (A, B) = {
  // foreach method  forces computation of elements of lazy collections
  // and doesn't delay any extra computations
  // so it's safe here even for LazyList
  seq.foreach { a =>
    val b = f(a)
    if (predicate(b)) return a -> b
  sys.error("no element found")

Yea, it should probably return an Option[(A,B)]. I noticed that after trying to implement it.

Yes. A saner variant then:

def cousinOfFind[A, B](seq:Findable[A], f:A => B, predicate: B => Boolean): Option[(A, B)] = {
  // foreach method  forces computation of elements of lazy collections
  // and doesn't delay any extra computations
  // so it's safe here even for LazyList
  seq.foreach { a =>
    val b = f(a)
    if (predicate(b)) return Some(a -> b)

Option is also strict and synchronous, thus it’s safe to use with non-local returns.

is Findable a real class? I just made it up.

There’s no Findable in stdlib:

Perhaps a bit cleaner.

def cousinOfFind[A,B](seq:Seq[A],f:A=>B,pred:B=>Boolean):Option[(A,B)] = { => (e, f(e))).find{case (_, y) => pred(y)}

Here’s some interesting historical background on early “return” statements. It turns out that Dijkstra’s warnings about them have been widely misunderstood.

“Single Exit” meant that a function should only return TO one place: the statement immediately following the call. It did not mean that a function should only return FROM one place. More below:

“Single Entry, Single Exit” was written when most programming was done in assembly language, FORTRAN, or COBOL. It has been widely misinterpreted, because modern languages do not support the practices Dijkstra was warning against.

“Single Entry” meant “do not create alternate entry points for functions”. In assembly language, of course, it is possible to enter a function at any instruction. FORTRAN supported multiple entries to functions with the ENTRY statement:

  R = SQRT(X*X + Y*Y)



CALL S(3,4)
CALL S2(5)

“Single Exit” meant that a function should only return to one place: the statement immediately following the call. It did not mean that a function should only return from one place. When Structured Programming was written, it was common practice for a function to indicate an error by returning to an alternate location. FORTRAN supported this via “alternate return”:

1 Like