将ValDef从编译转换为运行时

时间:2014-07-31 11:32:49

标签: scala reflection scala-macros

我想将ValDef用于运行时,但我不直接工作。如果我将ValDef封装到一个Block中,一切都很完美,如下例所示:

case class Container(expr: Expr[Any])

def lift(expr: Any): Container = macro reifyValDef

def reifyValDef(c: Context)(expr: c.Expr[Any]): c.Expr[Container] = {
  import c.universe._
  expr.tree match {
  case Block(List(v: ValDef), _) =>
    val asBlock = q"{$v}"
    val toRuntime = q"scala.reflect.runtime.universe.reify($asBlock)"
    c.Expr[Container](q"Container($toRuntime)")
  }
}

lift {
  val x: Int = 10
}

如果我直接使用 v ,而不是将其包装到一个块中,我会收到错误:

Error:(10, 11) type mismatch;
 found   : 
 required: Any
Note that  extends Any, not AnyRef.
Such types can participate in value classes, but instances
cannot appear in singleton types or in reference comparisons.
      val x: Int = 10
          ^

它是不是直接使用ValDefs还是我的代码有问题?

1 个答案:

答案 0 :(得分:0)

这是反射API中的已知问题之一。定义在技术上不是表达式,因此您不能例如直接将它们作为参数传递给函数。在块中包装定义是解决块的正确方法。

错误信息当然令人困惑,但确实有些歪曲。为了表示定义本身没有类型,相应tpe的{​​{1}}字段设置为Tree。然后针对NoType检查宏的参数类型,并且检查失败(因为Any是一种特殊类型,它与任何东西都不兼容),因此标准错误消息是打印。令人尴尬的打印输出是漂亮打印机在这种奇怪情况下的表现。