Scala编译时宏,基于(父)类型过滤参数列表

时间:2014-05-19 16:12:13

标签: scala macros types abstract-syntax-tree

我有一个方法/构造函数,它接受一堆参数。 使用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应该有一个“类型”? 我如何得到它以及我到底得到了什么?

2 个答案:

答案 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)
  }
}