Можно ли заменить метод применения по умолчанию, сгенерированный для классов в макросе Scala?
Кажется, что это не работает (с использованием 2.11.1 и macro paradise 2.0.1). Я надеялся, что методы, сгенерированные классом case, будут либо подавлены, либо будут находиться в дереве, чтобы я мог от него избавиться. Это жесткое ограничение?
class evis extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro EvisMacro.impl
}
object EvisMacro {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
import c.universe._
def makeApply(tpName: TypeName, parents: List[Tree], params: List[List[ValDef]] ) : List[Tree]= {
List(q"""def apply(...$params): $tpName = null""")
}
val result = annottees map (_.tree) match {
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
:: Nil if mods.hasFlag(Flag.CASE) =>
c.info(c.enclosingPosition, s"Eviscerating $tpname !($mods, $parents, $paramss)", true)
parents match {
case q"${pname: TypeName}" :: rest =>
c.info(c.enclosingPosition, s"${pname.decodedName}", true )
val sc = c.universe.rootMirror.staticClass( pname.decodedName.toString )
c.info(c.enclosingPosition, s"${sc}", true )
}
val name = tpname.toTermName
q"""
$classDef
object $name {
..${makeApply(tpname, parents, paramss)}
}
"""
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
:: q"object $objName {..$objDefs}"
:: Nil if mods.hasFlag(Flag.CASE) =>
q"""
$classDef
object $objName {
..${makeApply(tpname, parents, paramss)}
..$objDefs
}
"""
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a case class")
}
c.Expr[Any](result)
}
}
Используй это:
trait Thing
@evis
case class Trade(id: Long, notional: Long, comment: String) extends Thing
@evis
case class Pop(name: String) extends Thing
object Pop{
}
object TestTrade extends App{
val t = Trade (1, 1, "")
val p : Pop = Pop("")
println(t)
}
Результаты в:
Ошибка:(2, 2) метод apply определяется дважды конфликтующими символами, оба из которых находятся в файле 'core/src/main/scala/Test.scala' @evis ^
1 ответ
Проблема вызвана тем, что для компилятора код, сгенерированный аннотациями макросов, ничем не отличается от кода, написанного вручную. Если вы вручную напишите код, созданный макросом, представленным в примере, вы получите точно такую же ошибку двойного определения, так что это не ошибка - это ограничение синтеза класса случая. К сожалению, синтез классов случаев не является расширяемым, поэтому это необходимо обойти.
Один из обходных путей, который я бы предложил, это стереть CASE
флаг из модов класса, а затем макрос может быть полностью свободен в выборе членов для генерации. Это, однако, означает, что макрос должен будет генерировать весь код, который обычно генерируют классы case, что не будет очень приятным. Еще одна оговорка: компилятор обрабатывает сопоставление с образцом CASE
классы, в частности, испуская несколько более эффективный код, так что такая эмуляция также потеряет некоторую производительность (я считаю, что потеря будет незначительной, и, возможно, даже не существующей с новым механизмом сопоставления с шаблоном на основе имен из Scala 2.11 - но это должно быть проверено).