使用Scala宏生成方法
我想在Scala 2.11+中使用注释宏生成方法的别名。我甚至不确定这是否可能。如果是,如何进行 示例-鉴于以下内容,我希望注释宏扩展为使用Scala宏生成方法,scala,macros,scala-macros,scala-macro-paradise,scalameta,Scala,Macros,Scala Macros,Scala Macro Paradise,Scalameta,我想在Scala 2.11+中使用注释宏生成方法的别名。我甚至不确定这是否可能。如果是,如何进行 示例-鉴于以下内容,我希望注释宏扩展为 class Socket { @alias(aliases = Seq("!", "ask", "read")) def load(n: Int): Seq[Byte] = {/* impl */} } 我希望上面生成同义词方法存根,如下所示: class Socket { def load(n: Int): Seq[Byte] = // ...
class Socket {
@alias(aliases = Seq("!", "ask", "read"))
def load(n: Int): Seq[Byte] = {/* impl */}
}
我希望上面生成同义词方法存根,如下所示:
class Socket {
def load(n: Int): Seq[Byte] = // ....
def !(n: Int) = load(n)
def ask(n: Int) = load(n)
def read(n: Int) = load(n)
}
以上当然是一个有趣的例子,但我可以看到这种技术对于自动生成API的同步/异步版本或具有大量同义词的DSL非常有用。也可以在Scaladoc中公开这些生成的方法吗?使用Scala meta是否可以做到这一点
注意:我所问的与以下内容完全不同:
另外,请不要将此标记为重复,因为问题有点不同,而且Scala macro land在过去3年中发生了很多变化。这似乎不可能完全如前所述。在类成员上使用宏注释不允许操纵类本身的树。也就是说,当使用宏注释在类中注释方法时,将调用
macroTransform(annottees:Any*)
,但唯一的annottee将是方法本身
我能够通过两个注释获得概念证明。这显然不如简单地注释类那么好,但我想不出其他解决方法
您需要:
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
其思想是,可以使用此注释对每个方法进行注释,以便父类上的宏注释能够找到要展开的方法
class alias(aliases: String *) extends StaticAnnotation
然后宏:
// 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)
}
}
它还可以处理多个参数列表:
@aliased
class Foo {
@alias("bar", "baz")
def test(a: Int, b: Int)(c: String) = a + b + c
}
scala> val foo = new Foo
foo: Foo = Foo@3857a375
scala> foo.baz(1, 2)("4")
res0: String = 34
这似乎是可行的使用准。查看最近一次关于宏的会议上的幻灯片:出于某种原因,
代码>方法已被复制,但无法工作。我怀疑是因为这是一个特殊的角色,需要某种特殊的处理,但我现在没有时间去找出确切的原因。
@aliased
class Foo {
@alias("bar", "baz")
def test(a: Int, b: Int)(c: String) = a + b + c
}
scala> val foo = new Foo
foo: Foo = Foo@3857a375
scala> foo.baz(1, 2)("4")
res0: String = 34