2

我刚开始学习 scala 编译时反射,并被 Scala 官方指南介绍给了 quasiquotes。

reify我仍在苦苦挣扎的一个概念是,如果我想为已定义的对象生成 AST,我应该如何使用准引号(或者,就此而言)。假设我有一个对象:

object MyObject {
  def method1() = "m1"
}

为了得到一棵树,我知道我可以做到

q"""{object MyObject {
  def method1() = "m1"
}}
"""

然而,这样做会阻止我在我的范围内实际定义对象(而且我还需要将它完全定义在一个字符串中,从而将所有代码安全性抛到窗外)。

为了得到那棵树,我想做的是这样的:

object MyObject {
  def method1() = "m1"
}

q"$MyObject" // or q"{MyObject}", I still don't fully understand the table on the Scala guide

我想定义对象,然后使用该定义对其执行一些检查(如果需要,在编译时抛出一些异常),使用宏。据我了解,要使用宏,我需要树(或者至少是表达式)。

我已经知道如何在运行时使用 Scala 反射进行我想要的检查,但我认为使用 AST 可能是一个好主意(而且,在这个过程中,我会学到一些东西)。不过,我感觉我误解了一些关于如何使用 AST 的基本概念——似乎人们只能根据调用站点上声明的代码生成 AST。我很困惑。

我在这里有什么误解?

4

1 回答 1

2

准报价

q"""{object MyObject {
  def method1() = "m1"
}}
"""

或者

reify{
  object MyObject {
    def method1() = "m1"
  }
}.tree

只是写树的方法

Block(
  List(
    ModuleDef(Modifiers(), TermName("MyObject"), 
      Template(
        List(Select(Ident(scala), TypeName("AnyRef"))), 
        noSelfType, 
        List(
          DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), 
            Block(List(pendingSuperCall), Literal(Constant(())))
          ), 
          DefDef(Modifiers(), TermName("method1"), List(), List(List()), TypeTree(), 
            Literal(Constant("m1"))
          )
        )
      )
    )
  ),
  Literal(Constant(()))
)

可以从普通的context.parse(compile-time) / toolBox.parse(runtime)获得相同的String

val str: String = 
  """object MyObject {
    |  def method1() = "m1"
    |}""".stripMargin

toolBox.parse(str)

有宏的编译时间和宏的运行时间。主要代码及其运行时都有编译时间。宏的运行时间是主代码的编译时间。

MyObject

object MyObject {
  def method1() = "m1"
}

并且MyObject

q"""{object MyObject {
  def method1() = "m1"
}}
"""

存在于不同的语境中。前者存在于当前上下文中,后者存在于宏的调用点上下文中。

您可以将一棵树插入(拼接)到一棵树中。您不能将实际对象插入树中。如果您有实际对象(编译树),则将其插入树中为时已晚。

当您看到某物被插入到树中时,这意味着“某物”只是编写树的一种紧凑方式,即类型类的实例Liftable

object MyObject {
  def method1() = "m1"
}

implicit val myObjectLiftable: Liftable[MyObject.type] = new Liftable[MyObject.type] {
  override def apply(value: MyObject.type): Tree =
    q"""
      object MyObject {
        def method1() = "m1"
      }"""
}

q"""
   class SomeClass {
     $MyObject
   }"""

我猜你的宏看起来像

def foo[A](a: A) = macro impl[A]

或者

def foo[A] = macro impl[A]

所以你可以把它叫做foo(MyObject)orfoo[MyObject.type]和 inside

def impl[A: c.WeakTypeTag](c: blackbox.Context)...

您可以访问weakTypeOf[A],然后是它的符号。拥有符号,您可以拥有方法等的签名。

于 2020-06-18T22:51:04.127 回答