Bu tam mümkün görünmüyor. Bir sınıf üyesi üzerinde bir makro notu kullanmak, sınıfın ağacının kendisinde işlem yapmanıza izin vermez. Yani bir makro ek açıklamanın bulunduğu bir sınıf içinde bir yöntem açıklama yaparken, macroTransform(annottees: Any*)
adı verilecek, fakat sadece annottee yöntemi kendisi olacaktır.
İki ek açıklama ile çalışan bir ispat kanıtı elde edebildim. Belli ki, dersi sadece ders notu kadar güzel değil, ama etrafta başka bir şey düşünemiyorum.
Sen gerekir: ebeveyn sınıfının bir makro açıklama genişletmek istediğiniz hangi yöntemleri bulmak mümkün olacak şekilde
fikir
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
, sen bu açıklama ile her yöntemi açıklamalar oluşturabilirsiniz.
class alias(aliases: String *) extends StaticAnnotation
Ardından makro:
// Annotate the containing class to expand aliased methods within
@compileTimeOnly("You must enable the macro paradise plugin.")
class aliased extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AliasMacroImpl.impl
}
object AliasMacroImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val result = annottees map (_.tree) match {
// Match a class, and expand.
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }") :: _ =>
val aliasedDefs = for {
q"@alias(..$aliases) def $tname[..$tparams](...$paramss): $tpt = $expr" <- stats
Literal(Constant(alias)) <- aliases
ident = TermName(alias.toString)
} yield {
val args = paramss map { paramList =>
paramList.map { case q"$_ val $param: $_ = $_" => q"$param" }
}
q"def $ident[..$tparams](...$paramss): $tpt = $tname(...$args)"
}
if(aliasedDefs.nonEmpty) {
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
..$aliasedDefs
}
"""
} else classDef
// Not a class.
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: not a class")
}
c.Expr[Any](result)
}
}
bu uygulama kırılgan olacaktır unutmayın. Sadece, ilkinin ClassDef
olduğunu kontrol etmek için annotları denetler. Daha sonra, sınıfın üyelerine @alias
ile açıklamalı yöntemler ekler ve sınıfa yeniden eklenecek çok sayıda takma ağaç oluşturur. Açıklamalı herhangi bir yöntem yoksa, orijinal sınıf ağacını döndürür. Bu, yinelenen yöntem adlarını algılamayacak ve değiştiricileri yok edecektir (derleyici, ek açıklamaları ve değiştiricileri aynı anda eşleştirmeme izin vermeyecektir).
Bu
kolayca arkadaşı yanı nesneleri işlemek için genişletilmiş, ancak kod daha küçük tutmak için onları dışarı bırakılabilir. Kullandığım eşleştiriciler için
quasiquotes syntax summary'a bakın. Taşıma arkadaşı nesneler
case classDef :: objDef :: Nil
işlemek için
result
maçı ve dava
objDef :: Nil
değiştirerek gerektirecektir.Kullanımda
:
@aliased
class Socket {
@alias("ask", "read")
def load(n: Int): Seq[Byte] = Seq(1, 2, 3).map(_.toByte)
}
scala> val socket = new Socket
socket: Socket = [email protected]
scala> socket.load(5)
res0: Seq[Byte] = List(1, 2, 3)
scala> socket.ask(5)
res1: Seq[Byte] = List(1, 2, 3)
scala> socket.read(5)
res2: Seq[Byte] = List(1, 2, 3)
Ayrıca işleyebilir birden parametre listeleri:
quasiquotes kullanılarak yapılabilir görünüyor
@aliased
class Foo {
@alias("bar", "baz")
def test(a: Int, b: Int)(c: String) = a + b + c
}
scala> val foo = new Foo
foo: Foo = [email protected]
scala> foo.baz(1, 2)("4")
res0: String = 34
. Makrolarla ilgili yeni bir buluşmadan bu slayt destesine göz atın: https://speakerdeck.com/bwmcadams/ny-scala-meetup-scala-macros-or-how-i-learned-to-stop-worrying-and-mumble- wtf – JoseM