给定:
scala> def f(x: Int, y: Int): Int =
| if(x == 55) x else y
f: (x: Int, y: Int)Int
scala> def yFn: Int = {println("y"); 42}
yFn: Int
调用f(55, yFn)
会导致f
的评估,如y
打印输出所示。
scala> f(55, yFn)
y
res0: Int = 55
f
的签名如何保持不变:
(Int, Int) => Int
但是,是否必须在不将y
作为by-name
参数的情况下懒惰地计算y
?
将f
转换为宏将有效地执行您想要的操作:
import language.experimental.macros
import scala.reflect.macros.whitebox.Context
object Macros {
def f(x: Int, y: Int): Int = macro fImpl
def fImpl(c: Context)(x: c.Expr[Int], y: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
c.Expr(q"if ($x == 55) $x else $y")
}
}
f
的签名有Int
,而不是=> Int
,但调用f(55, yFn)
只会被if (55 == 55) 55 else yFn
替换,不会评估yFn
。
从这里你可以看到,任何不查看其参数结构的宏都会有效地按名称获取它们;如果你想确保一个参数只被计算一次,你需要把它分配给一个局部变量:q"{ val x = $x; if (x == 55) x else $y }"
。
但是,如果要将f
转换为对象而不是方法,这将丢失,因为生成的类的apply
方法不会是宏(它不可能;宏不能实现抽象方法(当然,或覆盖具体方法((。