在没有具体化的情况下制作代码时,Scala 宏不会编译

Scala macro does not compile when code is crafted without reify

我一直致力于复杂的编译时反射,并且发现需要使用 AST 手动编写 Scala 代码。在进行实验时,我注意到一个奇怪的编译错误,这对我来说并没有真正的意义,所以我尝试在一个测试项目中重现它。

我使用 Scala 2.10.4

代码如下:

Macro.scala:

object Macro {
  def reifyTestImpl(c: Context): c.Expr[OffsetDateTime] = {
    import c.universe._
    val expression = reify(OffsetDateTime.now())
    c.echo(c.enclosingPosition, "With reify: " + show(expression.tree))
    c.echo(c.enclosingPosition, "With reify (raw): " + showRaw(expression.tree))
    expression
  }

  def manualAstTestImpl(c: Context): c.Expr[OffsetDateTime] = {
    import c.universe._
    val odtSymbol = typeOf[OffsetDateTime].typeSymbol
    val now = newTermName("now")
    val expression = c.Expr(
      Apply(
        Select(Ident(odtSymbol), now),
        List()
      )
    )
    c.echo(c.enclosingPosition, "Manual:     " + show(expression.tree))
    c.echo(c.enclosingPosition, "Manual (raw):     " + showRaw(expression.tree))
    expression
  }

  def reifyTest = macro reifyTestImpl
  def manualAstTest = macro manualAstTestImpl
}

Tester.scala:

object Tester {
  def main(args: Array[String]): Unit = {
    println(Macro.reifyTest)
    println(Macro.manualAstTest)
  }
}

c.echo 的输出是:

With reify: OffsetDateTime.now()
With reify (raw): Apply(Select(Ident(java.time.OffsetDateTime), newTermName("now")), List())
Manual:     OffsetDateTime.now()
Manual (raw):     Apply(Select(Ident(java.time.OffsetDateTime), newTermName("now")), List())

我在调用 Macro.manualAstTest.
时得到的编译错误是 value now is not a member of java.time.OffsetDateTime 正如回声的输出所示,这两个表达式是相同的——但一个有效(来自 reify 的表达式)而另一个无效(使用 apply-select 制作的表达式)。

两者之间有什么区别?

设法找到罪魁祸首。
显然 typeOf[OffsetDateTime].typeSymbol return 是从 Scala class 中 return 编辑的符号,也就是说,没有它的静态成员。

添加 .companionSymbol 似乎 return 符号就像从 Scala 对象 return 编辑一样,也就是说,只有静态成员(顾名思义.. .)

因此,以下更改使其生效:

val odtSymbol = typeOf[OffsetDateTime].typeSymbol.companionSymbol