I want to implement a Scala macro that takes a partial function, performs some transformations in function templates, and then applies it to this expression.
To do this, I started with the following code:
def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = { import c.universe._ /* * Deconstruct the partial function and select the relevant case definitions. * * A partial, anonymus function will be translated into a new class of the following form: * * { @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[A,B] with Serializable { * * def <init>(): anonymous class $anonfun = ... * * final override def applyOrElse[...](x1: ..., default: ...): ... = ... match { * case ... => ... * case (defaultCase$ @ _) => default.apply(x1) * } * * def isDefined ... * } * new $anonfun() * }: PartialFunction[A,B] * */ val Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, allCaseDefs)), m)))), n), o) = patterns.tree /* Perform transformation on all cases */ val transformedCaseDefs: List[CaseDef] = allCaseDefs map { case caseDef => caseDef // This code will perform the desired transformations, now it just identity } /* Construct anonymus partial function with transformed case patterns */ val result = Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, transformedCaseDefs)), m)))), n), o) // println(show(result)) c.Expr[B](q"$result($expr)") }
I deconstruct the partial function, select the applyOrElse function case definitions, perform the required transformation on each definition, and add everything together. The macro is invoked as follows:
def myMatch[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro myMatchImpl[A,B]
Unfortunately, this does not work properly. Using a macro in a simple example
def test(x: Option[Int]) = myMatch(x){ case Some(n) => n case None => 0 }
The following error message appears:
object creation impossible, since method isDefinedAt in trait PartialFunction of type (x: Option[Int])Boolean is not defined
This is a bit confusing since printing the created partial function gives
({ final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Option[Int],Int] with Serializable { def <init>(): anonymous class $anonfun = { $anonfun.super.<init>(); () }; final override def applyOrElse[A1 <: Option[Int], B1 >: Int](x2: A1, default: A1 => B1): B1 = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match { case (x: Int)Some[Int]((n @ _)) => n case scala.None => 0 }; final def isDefinedAt(x2: Option[Int]): Boolean = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match { case (x: Int)Some[Int]((n @ _)) => true case scala.None => true case (defaultCase$ @ _) => false } }; new $anonfun() }: PartialFunction[Option[Int],Int])
which clearly defines the isDefinedAt method.
Does anyone have an idea what the problem is here and how to do it?