使用Scala宏生成方法
我想在
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] = // .... def !(n: Int) = load(n) def ask(n: Int) = load(n) def read(n: Int) = load(n) } 以上当然是一个很滑稽的例子,但我可以看到这种技术对于自动生成API的同步/异步版本或在具有大量同义词的DSL中很有用.是否也可以在Scaladoc中公开这些生成的方法?这可能是使用Scala meta吗? 注意:我要问的是:https://github.com/ktoso/scala-macro-method-alias 另外请不要将此标记为this的副本,因为问题有点不同,并且在过去3年中Scala宏观领域发生了很多变化. 解决方法
这似乎不完全如所述.在类成员上使用宏注释不允许您操作类本身的树.也就是说,当您使用宏注释在类中注释方法时,将调用macroTransform(annottees:Any *),但唯一的注释将是方法本身.
我能够通过两个注释获得概念验证.它显然不如简单地注释课程那么好,但我想不出另一种方法. 你需要: 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) } } 请记住,这种实现将是脆弱的.它只检查注释器,检查第一个是ClassDef.然后,它查找类的成员,这些成员是使用@alias注释的方法,并创建多个别名树以拼接回到类中.如果没有带注释的方法,它只返回原始的类树.因此,这不会检测重复的方法名称,并删除修饰符(编译器不会让我同时匹配注释和修饰符). 这可以很容易地扩展到处理伴随对象,但我把它们留下来以保持代码更小.有关我使用的匹配器,请参阅quasiquotes syntax summary.处理伴随对象需要修改结果匹配来处理case classDef :: objDef :: Nil和case objDef :: Nil. 正在使用: @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 = Socket@7407d2b8 scala> socket.load(5) res0: Seq[Byte] = List(1,3) scala> socket.ask(5) res1: Seq[Byte] = List(1,3) scala> socket.read(5) res2: Seq[Byte] = List(1,3) 它还可以处理多个参数列表: @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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |