gpt4 book ai didi

scala - 使用Scala宏生成方法

转载 作者:行者123 更新时间:2023-12-02 15:58:34 26 4
gpt4 key购买 nike

我想使用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。处理伴随对象将需要修改 result匹配项以处理 case classDef :: objDef :: Nil和大小写 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, 2, 3)

scala> socket.ask(5)
res1: Seq[Byte] = List(1, 2, 3)

scala> socket.read(5)
res2: Seq[Byte] = List(1, 2, 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

关于scala - 使用Scala宏生成方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33279472/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com