如何对先前定义的对象使用准引号
How to use quasiquotes with previously defined object
我刚开始研究scala 编译时反射,我是通过Scala 官方指南介绍了quasiquotes 的。
我仍在纠结的一个概念是,如果我想为一个已经定义的对象生成 AST,我应该如何使用准引号(或 reify
,就此而言)。假设我有一个对象:
object MyObject {
def method1() = "m1"
}
为了得到一棵树,我知道我可以做到
q"""{object MyObject {
def method1() = "m1"
}}
"""
然而,这样做会阻止我在我的范围内实际定义对象(而且我还需要将它完全定义在一个字符串中,从而使所有代码安全性都超出 window)。
我想要得到那棵树的方法是这样的:
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。我很困惑。
我在这里误解了什么?
准引用
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
(编译时)/toolBox.parse
(运行时)从普通的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"
}}
"""
存在于不同的语境中。前者存在于当前上下文中,后者存在于宏的调用点上下文中
您可以将一棵树插入(拼接)成一棵树。您不能将实际对象插入树中。如果您有实际对象(编译树),则将其插入树中为时已晚。
当您看到某些东西被插入到树中时,这意味着 "something" 只是编写树的一种紧凑方式,即 class Liftable
[=29 类型的实例=]
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)
或 foo[MyObject.type]
那样称呼它,在
里面
def impl[A: c.WeakTypeTag](c: blackbox.Context)...
您可以访问 weakTypeOf[A]
,然后是它的符号。有了符号,您就可以拥有方法签名等。
我刚开始研究scala 编译时反射,我是通过Scala 官方指南介绍了quasiquotes 的。
我仍在纠结的一个概念是,如果我想为一个已经定义的对象生成 AST,我应该如何使用准引号(或 reify
,就此而言)。假设我有一个对象:
object MyObject {
def method1() = "m1"
}
为了得到一棵树,我知道我可以做到
q"""{object MyObject {
def method1() = "m1"
}}
"""
然而,这样做会阻止我在我的范围内实际定义对象(而且我还需要将它完全定义在一个字符串中,从而使所有代码安全性都超出 window)。
我想要得到那棵树的方法是这样的:
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。我很困惑。
我在这里误解了什么?
准引用
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
(编译时)/toolBox.parse
(运行时)从普通的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"
}}
"""
存在于不同的语境中。前者存在于当前上下文中,后者存在于宏的调用点上下文中
您可以将一棵树插入(拼接)成一棵树。您不能将实际对象插入树中。如果您有实际对象(编译树),则将其插入树中为时已晚。
当您看到某些东西被插入到树中时,这意味着 "something" 只是编写树的一种紧凑方式,即 class Liftable
[=29 类型的实例=]
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)
或 foo[MyObject.type]
那样称呼它,在
def impl[A: c.WeakTypeTag](c: blackbox.Context)...
您可以访问 weakTypeOf[A]
,然后是它的符号。有了符号,您就可以拥有方法签名等。