如何检查一个类型是否是 Scala 宏中的某个特定泛型类型?
How to check if a type is some particular generic type in a Scala macro?
我想使用宏执行 AST 遍历,对于 AST 类型,如:
trait Node
case class Root(children: Seq[Node]) extends Node {
override def toString = s"Root(${children.size})"
}
case class Bi(left: Node, var right: Node) extends Node
case class Leaf(id: String) extends Node
在下面的代码中,我可以检测类型为 B
的 T
成员,并创建调用它们的函数。我还想检测类型为 Seq[B]
的成员(以及可能包含 B
的其他容器)。我已经尝试构造一个类型 TypeApply(Ident(TermName("Seq")), List(tq"$B"))
,它似乎可以工作,但是在这个 returns null
上调用 .tpe
,因此我不能在结果上调用 <:<
。
我该怎么做f.asMethod.returnType <:< Seq[B]
?
def walker[B, T <: B]: (T, B => Unit) => Unit = macro walker_impl[B, T]
def walker_impl[B: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[(T, B => Unit) => Unit] = {
import c.universe._
val T = weakTypeOf[T]
val B = weakTypeOf[B]
val seqType = TypeApply(Ident(TermName("Seq")), List(tq"$B"))
val dive = T.decls.collect {
case f if f.isMethod && f.asMethod.paramLists.isEmpty && f.asMethod.isGetter && f.asMethod.returnType <:< B =>
q"t.$f"
case f if f.isMethod && f.asMethod.paramLists.isEmpty &&
f.asMethod.isGetter && f.asMethod.returnType <:< seqType.tpe
=>
q"t.$f" // TODO: decompose the seq
}
c.Expr[(T, B => Unit) => Unit](
q"(t: $T, f: $B => Unit) => Seq(..$dive).map(f)"
)
}
我发现了一个图书馆正在做这样的事情,AVSystem/scala-commons。受这些来源的启发,我以这种方式实现了我想要的东西:
val iterable = typeOf[Iterable[Any]]
val iterableClass = iterable.typeSymbol
def isSeqB(returnType: Type) = {
returnType <:< iterable &&
returnType.baseType(iterableClass).typeArgs.headOption.exists(_ <:< B)
}
case f if f.isMethod && f.asMethod.paramLists.isEmpty && f.asMethod.isGetter &&
isSeqB(f.asMethod.returnType) =>
或简单
val iterableB = weakTypeOf[Iterable[B]]
case f if f.isMethod && f.asMethod.paramLists.isEmpty &&
f.asMethod.isGetter && f.asMethod.returnType <:< iterableB =>
我想使用宏执行 AST 遍历,对于 AST 类型,如:
trait Node
case class Root(children: Seq[Node]) extends Node {
override def toString = s"Root(${children.size})"
}
case class Bi(left: Node, var right: Node) extends Node
case class Leaf(id: String) extends Node
在下面的代码中,我可以检测类型为 B
的 T
成员,并创建调用它们的函数。我还想检测类型为 Seq[B]
的成员(以及可能包含 B
的其他容器)。我已经尝试构造一个类型 TypeApply(Ident(TermName("Seq")), List(tq"$B"))
,它似乎可以工作,但是在这个 returns null
上调用 .tpe
,因此我不能在结果上调用 <:<
。
我该怎么做f.asMethod.returnType <:< Seq[B]
?
def walker[B, T <: B]: (T, B => Unit) => Unit = macro walker_impl[B, T]
def walker_impl[B: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[(T, B => Unit) => Unit] = {
import c.universe._
val T = weakTypeOf[T]
val B = weakTypeOf[B]
val seqType = TypeApply(Ident(TermName("Seq")), List(tq"$B"))
val dive = T.decls.collect {
case f if f.isMethod && f.asMethod.paramLists.isEmpty && f.asMethod.isGetter && f.asMethod.returnType <:< B =>
q"t.$f"
case f if f.isMethod && f.asMethod.paramLists.isEmpty &&
f.asMethod.isGetter && f.asMethod.returnType <:< seqType.tpe
=>
q"t.$f" // TODO: decompose the seq
}
c.Expr[(T, B => Unit) => Unit](
q"(t: $T, f: $B => Unit) => Seq(..$dive).map(f)"
)
}
我发现了一个图书馆正在做这样的事情,AVSystem/scala-commons。受这些来源的启发,我以这种方式实现了我想要的东西:
val iterable = typeOf[Iterable[Any]]
val iterableClass = iterable.typeSymbol
def isSeqB(returnType: Type) = {
returnType <:< iterable &&
returnType.baseType(iterableClass).typeArgs.headOption.exists(_ <:< B)
}
case f if f.isMethod && f.asMethod.paramLists.isEmpty && f.asMethod.isGetter &&
isSeqB(f.asMethod.returnType) =>
或简单
val iterableB = weakTypeOf[Iterable[B]]
case f if f.isMethod && f.asMethod.paramLists.isEmpty &&
f.asMethod.isGetter && f.asMethod.returnType <:< iterableB =>