Skip to content

Instantly share code, notes, and snippets.

@buryat
Created January 10, 2018 19:00
Show Gist options
  • Save buryat/8d4b9e6e56195629e377cf7cbd6599e9 to your computer and use it in GitHub Desktop.
Save buryat/8d4b9e6e56195629e377cf7cbd6599e9 to your computer and use it in GitHub Desktop.
object TestReturn {
def t(n: Int): Boolean = {
(1 to n).map(x => {
if (x > 5) return true
false
}).exists(_ == true)
}
}
object TestReturn2 {
def t(n: Int): Boolean = {
(1 to n).map(x =>
if (x > 5) {
true
} else {
false
}
).exists(_ == true)
}
}
@buryat
Copy link
Author

buryat commented Jan 10, 2018

scalac -print return.scala

an explicit return gets converted into a scala.runtime.NonLocalReturnControl exception

package <empty> {
  object TestReturn extends Object {
    def t(n: Int): Boolean = {
      <synthetic> val nonLocalReturnKey1: Object = new Object();
      try {
        RichInt.this.to$extension0(scala.this.Predef.intWrapper(1), n).map({
  (new <$anon: Function1>(nonLocalReturnKey1): Function1)
}, immutable.this.IndexedSeq.canBuildFrom()).$asInstanceOf[collection.IterableLike]().exists({
          (new <$anon: Function1>(): Function1)
        })
      } catch {
        case (ex @ (_: scala.runtime.NonLocalReturnControl)) => if (ex.key().eq(nonLocalReturnKey1))
          ex.value$mcZ$sp()
        else
          throw ex
      }
    };
    def <init>(): TestReturn.type = {
      TestReturn.super.<init>();
      ()
    }
  };
  @SerialVersionUID(value = 0) final <synthetic> class anonfun$t$1 extends scala.runtime.AbstractFunction1$mcZI$sp with Serializable {
    final def apply(x: Int): Boolean = anonfun$t$1.this.apply$mcZI$sp(x);
    <specialized> def apply$mcZI$sp(x: Int): Boolean = {
      if (x.>(5))
        throw new scala.runtime.NonLocalReturnControl$mcZ$sp(anonfun$t$1.this.nonLocalReturnKey1$1, true)
      else
        ();
      false
    };
    final <bridge> <artifact> def apply(v1: Object): Object = scala.Boolean.box(anonfun$t$1.this.apply(scala.Int.unbox(v1)));
    <synthetic> <paramaccessor> private[this] val nonLocalReturnKey1$1: Object = _;
    def <init>(nonLocalReturnKey1$1: Object): <$anon: Function1> = {
      anonfun$t$1.this.nonLocalReturnKey1$1 = nonLocalReturnKey1$1;
      anonfun$t$1.super.<init>();
      ()
    }
  };
  @SerialVersionUID(value = 0) final <synthetic> class anonfun$t$2 extends scala.runtime.AbstractFunction1 with Serializable {
    final def apply(x$1: Boolean): Boolean = x$1.==(true);
    final <bridge> <artifact> def apply(v1: Object): Object = scala.Boolean.box(anonfun$t$2.this.apply(scala.Boolean.unbox(v1)));
    def <init>(): <$anon: Function1> = {
      anonfun$t$2.super.<init>();
      ()
    }
  }
}

while in the second case there're no exceptions

package <empty> {
  object TestReturn2 extends Object {
    def t(n: Int): Boolean = RichInt.this.to$extension0(scala.this.Predef.intWrapper(1), n).map({
  (new <$anon: Function1>(): Function1)
}, immutable.this.IndexedSeq.canBuildFrom()).$asInstanceOf[collection.IterableLike]().exists({
      (new <$anon: Function1>(): Function1)
    });
    def <init>(): TestReturn.type = {
      TestReturn.super.<init>();
      ()
    }
  };
  @SerialVersionUID(value = 0) final <synthetic> class anonfun$t$1 extends scala.runtime.AbstractFunction1$mcZI$sp with Serializable {
    final def apply(x: Int): Boolean = anonfun$t$1.this.apply$mcZI$sp(x);
    <specialized> def apply$mcZI$sp(x: Int): Boolean = if (x.>(5))
      true
    else
      false;
    final <bridge> <artifact> def apply(v1: Object): Object = scala.Boolean.box(anonfun$t$1.this.apply(scala.Int.unbox(v1)));
    def <init>(): <$anon: Function1> = {
      anonfun$t$1.super.<init>();
      ()
    }
  };
  @SerialVersionUID(value = 0) final <synthetic> class anonfun$t$2 extends scala.runtime.AbstractFunction1 with Serializable {
    final def apply(x$1: Boolean): Boolean = x$1.==(true);
    final <bridge> <artifact> def apply(v1: Object): Object = scala.Boolean.box(anonfun$t$2.this.apply(scala.Boolean.unbox(v1)));
    def <init>(): <$anon: Function1> = {
      anonfun$t$2.super.<init>();
      ()
    }
  }
}

@buryat
Copy link
Author

buryat commented Jan 10, 2018

also notice how the compiled code isn't entirely what we want, because return bubbles up into t and not into the anonymous function of map, so the result might not be correct

@buryat
Copy link
Author

buryat commented Jan 10, 2018

Here's an example of when it works not the way we expected

object TestReturn {
    def t(n: Int): Boolean = {
        (1 to n).map(x => {
            if (x == 5) return false
            true
        }).last
    }
}
object TestReturn2 {
    def t(n: Int): Boolean = {
        (1 to n).map(x =>
            if (x == 5) {
                false
            } else {
                true
            }
        ).last
    }
}
scala> object TestReturn {
     |     def t(n: Int): Boolean = {
     |         (1 to n).map(x => {
     |             if (x == 5) return false
     |             true
     |         }).last
     |     }
     | }
defined object TestReturn

scala> TestReturn.t(10)
res0: Boolean = false

But it should've returned true since we're past 5

scala> object TestReturn2 {
     |     def t(n: Int): Boolean = {
     |         (1 to n).map(x =>
     |             if (x == 5) {
     |                 false
     |             } else {
     |                 true
     |             }
     |         ).last
     |     }
     | }
defined object TestReturn2

scala> TestReturn2.t(10)
res1: Boolean = true

And this returns correct result, since it actually goes all the way to the last element

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment