我有一个方法/构造函数,它接受一堆参数。 使用scala宏我可以提取表示这些参数类型的Tree。
但是我无法找到如何将这棵树转换为“有用”的东西,即我可以获得父类型,检查它是否是原始的等等。
假设我有一个具体类型C
,如果希望所有继承自C
或的参数都是Seq[C]
的子类型。
对于一些上下文:
case cd@q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$_ } with ..$_ { $self => ..$stats }" :: tail =>
// extract everything that is subtype of C or Seq[C]
val cs = paramss.head.map {
case q"$mods val $name: $tpt = $default" => ???
}
宏观中的所有内容都应该进行类型检查,对吗?那么$ tpt应该有一个“类型”? 我如何得到它以及我到底得到了什么?
答案 0 :(得分:1)
这是我如何确定一个类型是继承自Iterable而不是来自Map,以及一个类型是否继承自Map
val iterableType = typeOf[Iterable[_]].typeSymbol
val mapType = typeOf[Map[_, _]].typeSymbol
def isIterable(tpe: Type): Boolean = tpe.baseClasses.contains(iterableType) && !isMap(tpe)
def isMap(tpe: Type): Boolean = tpe.baseClasses.contains(mapType)
我在例如类的主要构造函数中的字段:
// tpe is a Type
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val iterableFields = fields.filter(f => isIterable(f.typeSignature))
val mapFields = fields.filter(f => isMap(f.typeSignature))
答案 1 :(得分:0)
我还没有在愤怒中使用它们,但看起来你正在使用宏注释。
不,你得到无类型的记者。
http://docs.scala-lang.org/overviews/macros/annotations.html
宏注释是无类型的,因此我们可以更改其签名
鉴于客户:
package thingy
class C
@testThing class Thingy(val c: C, i: Int) { def j = 2*i }
object Test extends App {
Console println Thingy(42).stuff
}
然后宏实现看到:
val t = (annottees map (c typecheck _.tree)) match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$_ } with ..$_ { $self => ..$stats }" :: _ =>
val p @ q"$mods val $name: ${tpt: Type} = $default" = paramss.head.head
Console println showRaw(tpt)
Console println tpt.baseClasses
toEmit
}
显式地对树进行了类型检查,并在提取中注释了类型:
case q"val $name: ${tpt: Type} = ???" =>
我意识到我的annotee拼写与官方文档不同。我无法弄清楚为什么它应该与奉献者不同。
这是玩具骨架代码,适用于其他初学者:
package thingy
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
class testThing extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro testThing.impl
}
object testThing {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val toEmit = q"""{
class Thingy(i: Int) {
def stuff = println(i)
}
object Thingy {
def apply(x: Int) = new Thingy(x)
}
()
}"""
def dummy = Literal(Constant(()))
//val t = (annottees map (_.tree)) match {
val t = (annottees map (c typecheck _.tree)) match {
case Nil =>
c.abort(c.enclosingPosition, "No test target")
dummy
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$_ } with ..$_ { $self => ..$stats }" :: _ =>
//val p @ q"$mods val $name: $tpt = $default" = paramss.head.head
val p @ q"$mods val $name: ${tpt: Type} = $default" = paramss.head.head
Console println showRaw(tpt)
Console println tpt.baseClasses
toEmit
/*
case (classDeclaration: ClassDef) :: Nil =>
println("No companion provided")
toEmit
case (classDeclaration: ClassDef) :: (companionDeclaration: ModuleDef) :: Nil =>
println("Companion provided")
toEmit
*/
case _ => c.abort(c.enclosingPosition, "Invalid test target")
dummy
}
c.Expr[Any](t)
}
}