Last active
December 31, 2015 17:18
-
-
Save RSchulz/9bcec9b67861ba2965e0 to your computer and use it in GitHub Desktop.
Unexpected By-Name Parameter Evaluation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This code is a simplification (mostly the removal of type parameters) of an attempt, as an exercise, | |
to create a syntax that approximates the C- and Java-like conditional expression (“question-colon”). | |
The first attempt was eager, which is of course less than ideal. Next I tried to alter the code to | |
produce a form that evaluated only one of the value expressions. In doing so I found that the left- | |
hand (true) expression is always evaluated, even when the controlling Boolean is false. It appears | |
that the by-name parameter used to convey the left-hand value expression is being evaluated at the | |
point in the source where it appears (lexically). The right-hand (false case) expression is only | |
evaluated when the controlling Boolean is false and the stack trace indicates that the evalutation | |
is triggered where expected, in the body of TrueLeftFalseRight.right. | |
Can someone help me explain what’s going on? It seems like a bug, but I’m loathe to conclude that. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object CondExprLazyFail { | |
// Note: References in comments to type parameters reflect original generic code | |
/** The pair of left / true and right / false values | |
* | |
* @param leftVal The value to produce when the controlling boolean is '''true''' | |
* @param rightVal The value to produce when the controlling boolen is '''false''' | |
* @tparam T The type of ''left'' and ''right'' | |
*/ | |
class TrueLeftFalseRight(leftVal: => Int, rightVal: => Int) { | |
def left: Int = leftVal | |
def right: Int = rightVal | |
} | |
/** Wrap a right-hand / false-case value of type ''Int'' for pairing with its left-hand / true-case counterpart | |
* | |
* @param right The value that will become the right-hand / false-case member of the condintional expression | |
* @tparam T The type of ''right'' | |
*/ | |
implicit | |
class FalseRight(right: => Int) | |
{ | |
/** Pair this right-hand / false-case value with its left-hand / true-case counterpart | |
* | |
* @param left The value that will become the left-hand / true-case member of the condintional expression | |
* @tparam U The type of ''left'' (a supertype of [[T]]) | |
*/ | |
def ^: (left: => Int) = | |
new TrueLeftFalseRight(left, right) | |
} | |
/** Association between the controlling [[Boolean]] and the pair of values from which to select, based on its value | |
* | |
* @param cond The [[Boolean]] whose value controls the selection of the left / true or the right / false value | |
*/ | |
implicit | |
class CondExprLeft(val cond: Boolean) | |
extends AnyVal | |
{ | |
/** Choose which of the left / true or right / false values to produce | |
* | |
* @param rhs The pair of left-right / true-false values from which to select | |
* @tparam T The type of the values held in ''rhs'' | |
*/ | |
def |? (rhs: TrueLeftFalseRight): Int = | |
if (cond) rhs.left | |
else rhs.right | |
} | |
} | |
object CondExprFail { | |
import CondExprLazyFail._ | |
def main(args: Array[String]): Unit = { | |
Seq(false, true) foreach { b => | |
println(s"-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-\nb=$b:") | |
val v = | |
b |? | |
trueV ^: // <<-- IDEA signals by-name value here | |
falseV // <<-- but not here (for what it’s worth) | |
println(s"v[$b]=$v\n") | |
} | |
} | |
def trueV = { println("\ntrue => 123"); println(); new Exception("true").printStackTrace(); println(); 123 } | |
def falseV = { println("\nfalse => 321"); println(); new Exception("false").printStackTrace(); println(); 321 } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[info] Running CondExprFail | |
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- | |
b=false: | |
true => 123 | |
java.lang.Exception: true | |
at CondExprFail$.trueV(CondExprFail.scala:67) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:61) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:57) | |
at scala.collection.immutable.List.foreach(List.scala:381) | |
at CondExprFail$.main(CondExprFail.scala:57) | |
at CondExprFail.main(CondExprFail.scala) | |
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) | |
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) | |
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) | |
at java.lang.reflect.Method.invoke(Method.java:606) | |
at sbt.Run.invokeMain(Run.scala:67) | |
at sbt.Run.run0(Run.scala:61) | |
at sbt.Run.sbt$Run$$execute$1(Run.scala:51) | |
at sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Logger$$anon$4.apply(Logger.scala:85) | |
at sbt.TrapExit$App.run(TrapExit.scala:248) | |
at java.lang.Thread.run(Thread.java:745) | |
false => 321 | |
java.lang.Exception: false | |
at CondExprFail$.falseV(CondExprFail.scala:68) | |
at CondExprFail$$anonfun$main$1$$anonfun$1.apply$mcI$sp(CondExprFail.scala:62) | |
at CondExprLazyFail$TrueLeftFalseRight.right(CondExprFail.scala:13) | |
at CondExprLazyFail$CondExprLeft$.$bar$qmark$extension(CondExprFail.scala:48) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:61) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:57) | |
at scala.collection.immutable.List.foreach(List.scala:381) | |
at CondExprFail$.main(CondExprFail.scala:57) | |
at CondExprFail.main(CondExprFail.scala) | |
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) | |
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) | |
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) | |
at java.lang.reflect.Method.invoke(Method.java:606) | |
at sbt.Run.invokeMain(Run.scala:67) | |
at sbt.Run.run0(Run.scala:61) | |
at sbt.Run.sbt$Run$$execute$1(Run.scala:51) | |
at sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Logger$$anon$4.apply(Logger.scala:85) | |
at sbt.TrapExit$App.run(TrapExit.scala:248) | |
at java.lang.Thread.run(Thread.java:745) | |
v[false]=321 | |
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- | |
b=true: | |
true => 123 | |
java.lang.Exception: true | |
at CondExprFail$.trueV(CondExprFail.scala:67) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:61) | |
at CondExprFail$$anonfun$main$1.apply(CondExprFail.scala:57) | |
at scala.collection.immutable.List.foreach(List.scala:381) | |
at CondExprFail$.main(CondExprFail.scala:57) | |
at CondExprFail.main(CondExprFail.scala) | |
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) | |
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) | |
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) | |
at java.lang.reflect.Method.invoke(Method.java:606) | |
at sbt.Run.invokeMain(Run.scala:67) | |
at sbt.Run.run0(Run.scala:61) | |
at sbt.Run.sbt$Run$$execute$1(Run.scala:51) | |
at sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Run$$anonfun$run$1.apply(Run.scala:55) | |
at sbt.Logger$$anon$4.apply(Logger.scala:85) | |
at sbt.TrapExit$App.run(TrapExit.scala:248) | |
at java.lang.Thread.run(Thread.java:745) | |
v[true]=123 | |
[success] Total time: 3 s, completed Dec 31, 2015 9:14:20 AM | |
> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment