给定模块类,获取模块符号,scala宏 [英] Get the module symbol, given I have the module class, scala macro

查看:39
本文介绍了给定模块类,获取模块符号,scala宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用宏构建简单的类型类 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)
}

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

或者在我们的情况下简单

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆