Unapply / pattern matching с макросами scala и квазиквотами для knownDirectSubclasses
Я пытаюсь создать оператор match с помощью макросов, который соответствует всем подклассам данного типа. Но у меня есть проблемы с извлечением значений полей классов case. Например:
sealed abstract class Foobar
case class Foo(x:Int,f:Foobar) extends Foobar
case class Bar(s:String, f:Foobar) extends Foobar
Теперь я хочу создать код, который выглядит так, когда Foobar задан:
e1 match {
case Foo(args) => args.toString
case Bar(args) => args.toString
}
Вот что я получил до сих пор:
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)
}
Этот код будет соответствовать Foo и Bar, но я не могу извлечь поля, которые мне нужны с правой стороны.
1 ответ:
Так что у меня это в основном работает, вот пример:
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) } }
Этот код создает оператор match, который соответствует кортежу. Если обе стороны кортежа являются экземплярами одного и того же класса case, поля сравниваются. True возвращается, если все поля равны. Я знаю, что это не очень реалистичный пример, но я надеюсь, что это поможет. Для примера из вопроса это будет генерировать:
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 }