【问题标题】:How to check if a type is some particular generic type in a Scala macro?如何检查一个类型是否是 Scala 宏中的某个特定泛型类型?
【发布时间】:2020-06-07 08:13:10
【问题描述】:

我想使用宏执行 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

在下面的代码中,我可以检测到 T 的成员,这些成员的类型为 B,并创建一个调用它们的函数。我还想检测类型为Seq[B] 的成员(以及可能包含B 的其他容器)。我尝试构造一个类型TypeApply(Ident(TermName("Seq")), List(tq"$B")),它似乎可以工作,但是在此调用.tpe 返回null,因此我不能在结果上调用<:<

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)"
    )
  }

完整的项目可以在https://github.com/OndrejSpanel/ScalaMacroMembers看到

【问题讨论】:

    标签: scala types macros scala-macros


    【解决方案1】:

    我找到了一个库,它正在做这样的事情,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 =>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 2010-10-04
      • 2014-12-06
      相关资源
      最近更新 更多