【问题标题】:Get the module symbol, given I have the module class, scala macro获取模块符号,假设我有模块类,scala 宏
【发布时间】:2020-10-01 04:57:41
【问题描述】:

我正在尝试使用宏构建一个简单的类型类IsEnum[T]

如果T,我使用knownDirectSubclasses 获取所有直接子类,确保T 是密封特征,并且所有子类都是案例对象(使用subSymbol.asClass.isModuleClass && subSymbol.asClass.isCaseClass)。

现在我正在尝试使用子类引用的案例对象构建Seq

它正在工作,使用解决方法:

  Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])

但我从其他question 复制了它,但它看起来很老套和错误。为什么这行得通?有没有更清洁的方法来实现这一目标?

【问题讨论】:

  • 如果你不能使用 enumeratum(它为自己的类型提供Enum 类型类)我实现了Enum 类型类,它适用于:枚举、枚举、Java 枚举和案例对象的密封层次结构 - github.com/scalalandio/enumz 。有兴趣的可以看看那里的宏。
  • @MateuszKubuszok 我猜手动解析/类型检查可以替换为Ident(subSymbol.owner.info.decl(subSymbol.name.toTermName))
  • 很高兴知道,谢谢!

标签: scala enums scala-macros


【解决方案1】:

在 2.13 中你可以实现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

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中,他们使用一种

subSymbol.asClass.toType match {
  case ref @ TypeRef(_, sym, _) if sym.isModuleClass => mkAttributedQualifier(ref)
}

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/singletons.scala#L230

或者在我们的例子中只是

mkAttributedQualifier(subSymbol.asClass.toType)

但他们的mkAttributedQualifier 也使用向下转换到编译器内部,并且获得的树类似于Seq(A.this.B, A.this.C)

还有

Ident(subSymbol.companionSymbol)

似乎可以工作(树是 Seq(B, C))但 .companionSymbol 已被弃用(在 scaladocs 中它被写成“可能为模块类返回意外结果”,即对象)。

以下方法类似于 @MateuszKubuszok 在他的库中使用的方法enumz 你也可以尝试

val objectName = symbol.fullName
c.typecheck(c.parse(s"$objectName"))

树是Seq(App.A.B, App.A.C)

最后,如果您对树 Seq(B, C)(而不是更复杂的树)感兴趣,您似乎可以替换

Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])

更传统的

Ident(subSymbol.owner.info.decl(subSymbol.name.toTermName))

【讨论】:

  • @AlejandroNavas 在 2.12 中可以使用 shapeless.Witness 代替 ValueOf。查看更新。
  • @AlejandroNavas 我想我找到了。试试Ident(symbol.owner.info.decl(symbol.name.toTermName))。查看更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-23
  • 2011-11-28
  • 2017-11-23
  • 2011-02-12
  • 2011-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多