在 Scala 宏中,如何在编译时提升一个对象并在准引用中使用它?
In scala macro, how to lift an object and use it in quasiquote at compile time?
以下代码片段是来自 thoughtworks 项目的简短 Scala 宏包定义:
private[SelfType] final class Macros(val c: whitebox.Context) {
import c.universe._
def apply[A: WeakTypeTag]: Tree = {
val a = weakTypeOf[A]
val selfTypes: List[Type] = {
val selfTypeBuilder = List.newBuilder[Type]
def buildSelfTypes(t: Type): Unit = {
val dealiased = t.dealias
dealiased match {
case RefinedType(superTypes, refinedScope) =>
superTypes.foreach(buildSelfTypes)
case typeRef: TypeRef =>
val symbol = dealiased.typeSymbol
if (symbol.isClass) {
selfTypeBuilder += symbol.asClass.selfType.asSeenFrom(dealiased, symbol)
}
case _ =>
}
}
buildSelfTypes(a)
selfTypeBuilder.result()
}
val out = selfTypes match {
case Nil =>
definitions.AnyTpe
case _ =>
internal.refinedType(selfTypes, c.internal.enclosingOwner)
}
q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
}
}
作为准引用的最后一行似乎包含很多样板文本:
q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
假设这个宏包是在特征内部定义的,作为家族多态设计模式的一部分:没有确定性q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
,它必须从对象变量vvv
派生编译时的宏包。我如何使用此变量使准引用更短且更具适应性?
可能有多种方法可以实现这一点(例如,对于每个实现,为 SelfType
对象定义一个 Liftable)。但这更多的是样板文件。我正在寻找最短的解决方案。理想情况下,像这样:
val sym = Term(vvv)
q"$sym.make[$a, $out]"
找到我的第一个解决方案:
val name = SelfTypes.getClass.getCanonicalName.stripSuffix("$")
val tree = c.parse(name)
q"$tree.make[$a, $out]"
不确定它是否是最有效或最惯用的解决方案,我会让这个问题暂时搁置
如果你有一个静态引用(例如 type/companion 被导入),你可以这样做:
q"${symbolOf[SelfType.type]}.make[$a, $out]"
如果您有 A: WeakTypeTag
但没有关于其同伴的信息,您也可以使用 symbolOf[A].companion
。如果编译器不认为 object A
是 class A
的同伴,那可能行不通
以下代码片段是来自 thoughtworks 项目的简短 Scala 宏包定义:
private[SelfType] final class Macros(val c: whitebox.Context) {
import c.universe._
def apply[A: WeakTypeTag]: Tree = {
val a = weakTypeOf[A]
val selfTypes: List[Type] = {
val selfTypeBuilder = List.newBuilder[Type]
def buildSelfTypes(t: Type): Unit = {
val dealiased = t.dealias
dealiased match {
case RefinedType(superTypes, refinedScope) =>
superTypes.foreach(buildSelfTypes)
case typeRef: TypeRef =>
val symbol = dealiased.typeSymbol
if (symbol.isClass) {
selfTypeBuilder += symbol.asClass.selfType.asSeenFrom(dealiased, symbol)
}
case _ =>
}
}
buildSelfTypes(a)
selfTypeBuilder.result()
}
val out = selfTypes match {
case Nil =>
definitions.AnyTpe
case _ =>
internal.refinedType(selfTypes, c.internal.enclosingOwner)
}
q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
}
}
作为准引用的最后一行似乎包含很多样板文本:
q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
假设这个宏包是在特征内部定义的,作为家族多态设计模式的一部分:没有确定性q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"
,它必须从对象变量vvv
派生编译时的宏包。我如何使用此变量使准引用更短且更具适应性?
可能有多种方法可以实现这一点(例如,对于每个实现,为 SelfType
对象定义一个 Liftable)。但这更多的是样板文件。我正在寻找最短的解决方案。理想情况下,像这样:
val sym = Term(vvv)
q"$sym.make[$a, $out]"
找到我的第一个解决方案:
val name = SelfTypes.getClass.getCanonicalName.stripSuffix("$")
val tree = c.parse(name)
q"$tree.make[$a, $out]"
不确定它是否是最有效或最惯用的解决方案,我会让这个问题暂时搁置
如果你有一个静态引用(例如 type/companion 被导入),你可以这样做:
q"${symbolOf[SelfType.type]}.make[$a, $out]"
如果您有 A: WeakTypeTag
但没有关于其同伴的信息,您也可以使用 symbolOf[A].companion
。如果编译器不认为 object A
是 class A