简体   繁体   English

与scala宏和准引号的undirectly / pattern匹配为knownDirectSubclasses

[英]Unapply/pattern matching with scala macros and quasiquotes for knownDirectSubclasses

I'm trying to create a match statement using macros, that matches all subclasses of a given type. 我正在尝试使用宏来创建一个match语句,该语句匹配给定类型的所有子类。 But I have trouble with extracting the field values of the case classes. 但是我在提取案例类的字段值时遇到了麻烦。 For example: 例如:

sealed abstract class Foobar
case class Foo(x:Int,f:Foobar) extends Foobar
case class Bar(s:String, f:Foobar) extends Foobar

Now I want to create code that looks like this, when Foobar is given: 现在,当给定Foobar时,我想创建如下代码:

 e1 match {
   case Foo(args) => args.toString
   case Bar(args) => args.toString
 }

Thats what I got so far: 那就是我到目前为止所得到的:

  def eqImpl[A: c.WeakTypeTag](c: Context)(e1: c.Expr[A], e2: c.Expr[A]): c.Expr[Boolean] = {
    import c.universe._

    val tpe = c.weakTypeOf[A].typeSymbol.asClass
    tpe.typeSignature // SI-7046
    val subclasses = tpe.knownDirectSubclasses

    val cases = 
      subclasses.map{ clazz =>
      cq"x: $clazz => x " 
    }
    println(cases)
    reify(true)
  }

This code would match Foo and Bar, but I can not extract the fields, which I need on the right hand side. 该代码将匹配Foo和Bar,但我无法提取右侧需要的字段。

So I got it mostly working, here is an example: 所以我得到了大部分的工作,这是一个例子:

def eqImpl[A: c.WeakTypeTag](c: Context)(e1: c.Expr[A], e2: c.Expr[A]): c.Expr[Boolean] = {
    import c.universe._
    val tpe = c.weakTypeOf[A].typeSymbol.asClass
    tpe.typeSignature // SI-7046 workaround 

    val subclasses = tpe.knownDirectSubclasses

    val cases = 
      subclasses.map{ case clazz : ClassSymbol =>
        require (clazz.isCaseClass)
        val name = clazz.companionSymbol.name
        val fields = clazz.typeSignature.declarations.collect {
        case m: MethodSymbol if m.isCaseAccessor => m.name}
        //pattern for the fields of the left and right side side
        val lFields = fields.map{ m => pq"""${m+"L":TermName}"""} 
        val rFields = fields.map{ m => pq"""${m+"R":TermName}"""} side
        //right hand side of the case statment 
        val eqFields = 
          fields.map{ m => q"""${m+"R":TermName} == ${m+"L":TermName}"""}.reduce[Tree]{ 
            case (acc,n) => q"$acc && $n"}

        cq"($name(..$lFields),$name(..$rFields)) => $eqFields  " 
    }
    val matchStmt = q"""Tuple2[$tpe,$tpe]($e1,$e2) match {
          case ..$cases
          case _ => false }"""
    c.Expr[Boolean](matchStmt)
  }
}

This code creates a match statement, that matches a tuple. 这段代码创建了一个匹配元组的match语句。 If both sides of the tuple are instances of the same case class, the fields are compared. 如果元组的两端都是相同case类的实例,则将比较字段。 True is returned, if all fields are equal. 如果所有字段均相等,则返回True。 I know that is not a particularly realistic example, but I hope it helps. 我知道这不是一个特别现实的例子,但我希望它会有所帮助。 For the example from the question this would generate: 对于该问题的示例,将生成:

Tuple2[Foobar,Foobar](e1,e2) match {
     case (Foo(xL,fL),Foo(xR,fR) => xL == xR && fL == fR
     case (Bar(sL,fL),Bar(sR,fR) => sL == sR && fL == fR
     case _                      => false
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM