问题描述
是否可以使用宏为包下的每个类返回 TypeSymbol
的 List
列表的方法?
Is there a way to return a List
of TypeSymbol
s for each class under a package using macros?
我要实现的目标是编写一个宏,该宏给出与该列表等效的内容:
What I am trying to achieve is to write a macro that gives out something equivalent to this list:
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> case class MyClass1()
defined class MyClass1
scala> case class MyClass2()
defined class MyClass2
scala> val typeSymbols = List(typeOf[MyClass1].typeSymbol, typeOf[MyClass2].typeSymbol)
typeSymbols: List[reflect.runtime.universe.Symbol] = List(class MyClass1, class MyClass2)
这是我的设置:
我有一个名为 foo
的程序包,在该程序包下定义了这些程序包:
I have a package named foo
, under which these are defined:
trait FooTrait
case class Bar() extends FooTrait
case class Bar() extends FooTrait
这是我的宏,它获取foo下扩展 FooTrait
的类的所有类型符号:
Here's my macro that gets all type symbols for the classes under foo that extend FooTrait
:
def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
// Get package name from the expression tree
val Literal(Constant(name: String)) = packageName.tree
// Get all classes under given package name
val pkg = c.mirror.staticPackage(name)
// Obtain type symbols for the classes - implementation omitted
val types = getTypeSymbols(c.universe)(List(pkg))
// Apply method for List. For easy readability in later applications
val listApply = Select(reify(List).tree, newTermName("apply"))
val result = types.map {
t =>
val typeName = c.Expr[TypeSymbol](Ident(t))
println(s"Typename: $typeName, $t, ${t.toType}")
reify(typeName.splice).tree
}
println(s"RESULT: ${showRaw(result)}")
c.Expr[List[reflect.runtime.universe.TypeSymbol]](Apply(listApply, result.toList))
}
第一个 println
打印:
Typename: Expr[c.universe.TypeSymbol](Bar), class Bar, foo.Bar
Typename: Expr[c.universe.TypeSymbol](Baz), class Baz, foo.Baz
第二张照片打印:
RESULT: List(Ident(foo.Bar), Ident(foo.Baz))
但是我收到此错误消息:
But I get this error message:
[error] no type parameters for method any2ArrowAssoc: (x: A)ArrowAssoc[A] exist so that it can be applied to arguments (<notype>)
[error] --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error] found : <notype>
[error] required: ?A
[error] Note that <none> extends Any, not AnyRef.
[error] Such types can participate in value classes, but instances
[error] cannot appear in singleton types or in reference comparisons.
我应该怎么做才能使它工作?我怀疑我必须写其他东西代替 Ident
,但是我不知道是什么.
What should I do to make this work? I suspect that I have to write something else instead of Ident
, but I couldn't figure out what.
使用Scala 2.10.2.
Using Scala 2.10.2.
提前谢谢!
推荐答案
您必须使用 reifyType
可以在运行时Universe中创建反射工件:
You have to use reifyType
to create reflection artifacts in the runtime universe:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object PackageMacros {
def allTypeSymbols[T](packageName: String) = macro allTypeSymbols_impl[T]
def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(
packageName: c.Expr[String]
) = {
import c.universe._
val pkg = packageName.tree match {
case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
}
val types = pkg.typeSignature.members.collect {
case sym: ClassSymbol =>
c.reifyType(treeBuild.mkRuntimeUniverseRef, EmptyTree, sym.toType)
}.toList
val listApply = Select(reify(List).tree, newTermName("apply"))
c.Expr[List[Any]](Apply(listApply, types))
}
}
这将为您提供类型标签列表,而不是符号,但是您可以很容易地获得符号,就像这样:
This will give you a list of type tags, not symbols, but you can pretty easily get the symbols, either like this:
scala> PackageMacros.allTypeSymbols("foo").map(_.tpe.typeSymbol) foreach println
class Baz$
class Bar
class Baz
trait FooTrait
class Bar$
或者在宏本身中.
这篇关于Scala宏:获取要在运行时使用的TypeSymbol的列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!