MacroAnnotation для удаления аннотации из члена типа метода

Я учусь писать макросы Scala и написал аннотацию макроса, которая удаляет аннотацию из параметра типа аннотированной функции. Вот.

Аннотация для удаления:

class garbage extends StaticAnnotation

Реализация макроса для удаления аннотации:

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

Метод тестирования:

trait Test{
  @removeGarbage
  def someMethod[@garbage Source, G[_]](i: Int): G[List[Int]]
}

Кажется, это работает нормально. Чтобы проверить это, я сравнил лог, добавленный с println(annottees) и println(expandees):

List(def someMethod[@new garbage() Source, G[_]](i: Int): G[List[Int]])
List(def someMethod[Source, G[_]](i: Int): G[List[Int]])

Проблема решения заключается в том, что его трудно читать. Возможно, я не использовал квазицитаты в полной мере. Есть ли способ упростить реализацию макроса (возможно, более широко используя квазикавычки...)?


person Some Name    schedule 30.10.2020    source источник


Ответы (1)


Это нормально, когда код макроса трудно читать :) Вот почему метапрограммирование не должно быть инструментом №1.

Я не понимаю, как можно значительно сократить ваш код.

Вы можете заменить

val modifiedMods = mods match {
  case Modifiers(flags, privateWithin, annots) =>
    Modifiers(flags, privateWithin, annots.filter(_ == q"new garbage()"))
}

с однострочным

val modifiedMods = mods.mapAnnotations(_.filter(_ == q"new garbage()"))

Если вы продолжаете выполнять один и тот же набор преобразований во многих макросах, вы можете аналогичным образом определить вспомогательные методы, такие как mapDef, mapTypeParams...

Если квазицитаты становятся слишком громоздкими, вы можете рассмотреть использование ClassDef, Template, DefDef ... вместо квазикавычек или смешивайте их с квазикавычками, когда это удобно.

(Такие вопросы обычно относятся к https://codereview.stackexchange.com/, хотя метапрограммирование кажется не поэтому популярен там.)

person Dmytro Mitin    schedule 30.10.2020