Skip to content

Instantly share code, notes, and snippets.

@RSchulz
Last active December 31, 2015 17:18
Show Gist options
  • Save RSchulz/9bcec9b67861ba2965e0 to your computer and use it in GitHub Desktop.
Save RSchulz/9bcec9b67861ba2965e0 to your computer and use it in GitHub Desktop.
Unexpected By-Name Parameter Evaluation
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.
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 }
}
[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