I'm learning how to write Scala Macros and wrote a macro annotation that removes an annotation from a type parameter of an annotated function. Here it is.
The annotation to remove:
class garbage extends StaticAnnotation
Implementation of macro to remove the annotation:
@compileTimeOnly("Compile-time only annotation")
class removeGarbage extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro impl
}
object removeGarbage {
def impl(c: whitebox.Context)(annottees: c.Tree*) = {
import c.universe._
println(annottees)
val expandee = annottees.toList collect {
case q"$mods def $templatename[..$typeparams](...$paramss): $tpt = $body" =>
val modifiedParams = typeparams collect {
case q"$mods type $name[..$args] = $tpt" =>
val modifiedMods = mods match {
case Modifiers(flags, privateWithin, annots) =>
Modifiers(flags, privateWithin, annots.filter(_ == q"new garbage()"))
}
q"$modifiedMods type $name[..$args] = $tpt"
}
q"$mods def $templatename[..$modifiedParams](...$paramss): $tpt = $body"
case annottee =>
c.abort(c.enclosingPosition, s"$annottee cannot be annotated with @removeGarbage. Only def methods are allowed")
}
println(expandee)
q"..$expandee"
}
}
Test method:
trait Test{
@removeGarbage
def someMethod[@garbage Source, G[_]](i: Int): G[List[Int]]
}
That seem to work fine. To check it I compared the log added with println(annottees)
and println(expandees)
:
List(def someMethod[@new garbage() Source, G[_]](i: Int): G[List[Int]])
List(def someMethod[Source, G[_]](i: Int): G[List[Int]])
The problem about the solution is it looks difficult to read. Maybe I didn't use quasiquotes to their full potential. Is there a way to simplify the macro implementation (probably using quasiquotes more extensively...)?
That's ok for a macro code to be difficult to read :) This is why metaprogramming shouldn't be the tool #1.
I can't see how your code can be reduced significantly.
You can replace
val modifiedMods = mods match {
case Modifiers(flags, privateWithin, annots) =>
Modifiers(flags, privateWithin, annots.filter(_ == q"new garbage()"))
}
with one-liner
val modifiedMods = mods.mapAnnotations(_.filter(_ == q"new garbage()"))
If you keep doing the same set of transformations in many macros you can similarly define helper methods like mapDef
, mapTypeParams
...
If quasiquotes become too cumbersome you can consider to use ClassDef
, Template
, DefDef
... instead of quasiquotes or mix them with quasiquotes when convenient.
(Such questions are normally for https://codereview.stackexchange.com/ although metaprogramming seems to be not so popular there.)