我正在编写一个 Scala 宏 (Scala 2.11),我想在其中使用inferImplicitValue
获取表示宏内隐变量的树,评估该语法树,并使用该值。 我实际上已经这样做了,但它似乎并非在所有情况下都有效[1]。 我构建了一个失败的简化示例。
// a class for implicit evidence
class DemoEvidence(val value: Int)
// define 'foo' method for invoking the macro
object demoModule {
def foo: Int = macro DemoMacros.fooImpl
}
class DemoMacros(val c: whitebox.Context) {
import c.universe._
def fooImpl: Tree = {
val vInt = try {
// get the tree representing the implicit value
val impl = c.inferImplicitValue(typeOf[DemoEvidence], silent = false)
// print it out
println(s"impl= $impl")
// try to evaluate the tree (this is failing)
val eval = c.eval(c.Expr[DemoEvidence](c.untypecheck(impl.duplicate)))
eval.value
} catch {
case e: Throwable => {
// on failure print out the failure message
println(s"Eval failed with: $enStack trace:n${e.printStackTrace}")
0
}
}
q"$vInt" // return tree representing the integer value
}
}
如果我编译上述内容,然后调用它:
object demo {
implicit val demoEvidence: DemoEvidence = new DemoEvidence(42)
val i: Int = demoModule.foo
}
我看到编译失败的方式如下:
impl= demo.this.demoEvidence
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$compile$1.apply(ToolBoxFactory.scala:275)
...
完整输出位于: https://gist.github.com/erikerlandson/df48f64329be6ab9de9caef5f5be4a83
因此,您可以看到它正在为声明的隐式值demo.this.demoEvidence
查找树,但是对该树的评估失败。 我已经看到这种基本方法在我的项目的其他地方有效。 不确定有什么区别,以及为什么它在这里失败。
[1] 更新:如果隐式值在(子)项目中定义,并编译,然后在该项目外部使用,则它按预期工作。这就是这种方法对我有用的情况。
所以问题是这是否只是我必须忍受的基本约束,或者是否有一些聪明的解决方法,或者这是否是一个"错误",在可能被修复的宏中推断隐式值。
更新:我为此提交了 Scala 问题:https://github.com/scala/scala-dev/issues/353
从堆栈跟踪的外观来看,eval
期望object demo
以类文件形式存在以供执行,这是有道理的,因为您尝试计算的值取决于val demoEvidence
哪个是object demo
的成员。
但是eval
是在object demo
的类型检查期间发生的,因此类文件尚不存在,因此出现错误。在具有子项目中定义的隐式值的版本中,我想子项目首先被编译,因此eval
所需的类文件存在,因此评估按您的预期进行。