给定模块类,获取模块符号,scala宏 [英] Get the module symbol, given I have the module class, scala macro
问题描述
我正在尝试使用宏构建简单的类型类 IsEnum [T]
.
I'm trying to build a simple typeclass IsEnum[T]
using a macro.
如果 T
,我使用 knownDirectSubclasses
来获取所有直接子类,确保 T
是密封特征,并且所有子类都是大小写对象(使用 subSymbol.asClass.isModuleClass&& subSymbol.asClass.isCaseClass
).
I use knownDirectSubclasses
to get all the direct subclasses if T
, ensure T
is a sealed trait, and that all subclasses are of case objects (using subSymbol.asClass.isModuleClass && subSymbol.asClass.isCaseClass
).
现在,我正在尝试使用子类引用的case对象构建 Seq
.
Now I'm trying to build a Seq
with the case objects referred by the subclasses.
它正在工作,并且有一种解决方法:
It's working, using a workaround:
Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
但是我从其他一些问题中复制了它,似乎很老套和错误.为什么行得通?有没有更清洁的方法来实现这一目标?
But I copied that from some other question, yet it seems hacky and wrong. Why does that work? and is there a cleaner way to achieve that?
推荐答案
在2.13中,您可以实现 scala.ValueOf
In 2.13 you can materialize scala.ValueOf
val instanceTree = c.inferImplicitValue(appliedType(typeOf[ValueOf[_]].typeConstructor, subSymbol.asClass.toType))
q"$instanceTree.value"
树会有所不同
sealed trait A
object A {
case object B extends A
case object C extends A
}
//scalac: Seq(new scala.ValueOf(A.this.B).value, new scala.ValueOf(A.this.C).value)
但在运行时仍为 Seq(B,C)
.
在2.12版本中 shapeless.Witness
可以代替 ValueOf
In 2.12 shapeless.Witness
can be used instead of ValueOf
val instanceTree = c.inferImplicitValue(appliedType(typeOf[Witness.Aux[_]].typeConstructor, subSymbol.asClass.toType))
q"$instanceTree.value"
//scalac: Seq(Witness.mkWitness[App.A.B.type](A.this.B.asInstanceOf[App.A.B.type]).value, Witness.mkWitness[App.A.C.type](A.this.C.asInstanceOf[App.A.C.type]).value)
libraryDependencies += "com.chuusai" %% "shapeless" % "2.4.0-M1" // in 2.3.3 it doesn't work
在Shapeless中,他们使用了
In Shapeless they use kind of
subSymbol.asClass.toType match {
case ref @ TypeRef(_, sym, _) if sym.isModuleClass => mkAttributedQualifier(ref)
}
或者在我们的情况下简单
or in our case simply
mkAttributedQualifier(subSymbol.asClass.toType)
,但是他们的 mkAttributedQualifier
也使用向下转换来编译器内部,并且获得的树类似于 Seq(A.this.B,A.this.C)
.
but their mkAttributedQualifier
also uses downcasting to compiler internals and the tree obtained is like Seq(A.this.B, A.this.C)
.
也
Ident(subSymbol.companionSymbol)
似乎可以工作(树是 Seq(B,C)
),但不推荐使用 .companionSymbol
(在scaladocs中,其写为可能为模块类返回意外结果").即用于对象).
seems to work (tree is Seq(B, C)
) but .companionSymbol
is deprecated (in scaladocs it's written "may return unexpected results for module classes" i.e. for objects).
与 @MateuszKubuszok 在其库枚举中使用的方法类似.a>您也可以尝试
Following approach similar to the one used by @MateuszKubuszok in his library enumz you can try also
val objectName = symbol.fullName
c.typecheck(c.parse(s"$objectName"))
,树是 Seq(App.A.B,App.A.C)
.
最后,如果您对树 Seq(B,C)
(而不是更复杂的树)感兴趣,似乎可以替换
Finally, if you're interested in the tree Seq(B, C)
(and not some more complicated tree) it seems you can replace
Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
更常规
Ident(subSymbol.owner.info.decl(subSymbol.name.toTermName))
这篇关于给定模块类,获取模块符号,scala宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!