使用Scala宏生成方法

2023-11-24

我想在 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 元可以实现这一点吗?

注意:我要问的与以下内容完全不同: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,并创建多个别名树以拼接回类中。如果没有带注释的方法,它只是返回原始的类树。事实上,这不会检测重复的方法名称,并删除修饰符(编译器不会让我同时匹配注释和修饰符)。

这也可以轻松扩展以处理伴随对象,但我将它们保留下来以保持代码更小。请参阅准引号语法总结对于我使用的匹配器。处理伴随对象需要修改result匹配处理case classDef :: objDef :: Nil,和案例objDef :: Nil.

In use:

@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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Scala宏生成方法 的相关文章

随机推荐

  • 无法将 DaoAuthenticationConfigurer 应用于已构建的对象

    我收到这个异常 WARN org springframework web context support GenericWebApplicationContext Exception encountered during context i
  • 使用 Java+Scala+Slick2D 时“无法从给定的启动配置中找到主要方法”

    我有一个使用 Java Scala Slick2D 的项目 当从 eclipse 中启动时 该项目本身运行良好 但是当我尝试制作 jar 文件时 它就是拒绝工作 这是我在尝试将其导出为 Runnable jar 时不断收到的错误 如果我尝试
  • 将文件从 s3 存储桶传输到亚马逊 RDS 数据库

    我正在尝试将数据从 s3 存储桶加载到亚马逊 RDS 数据库 我知道这不是编程问题 但我真的很感谢帮助 我使用了下面的代码 aws rds restore db instance from s3 allocated storage 250
  • Django .filter 具有多种可能性的同一选项

    我有一个对象模型 我还有一个用于过滤结果的选项列表 我不确定是否有一种简单的方法来过滤模型中的对象 以便返回与过滤器列表中的任何项目匹配的任何对象 例如 returns all users with name starting with P
  • Xcode 界面生成器。这些自动调整大小蒙版设置有何不同?

    我现在很习惯在 IB 中使用自动调整大小蒙版 但是有两个自动调整大小设置 我不清楚它们有何不同 设置1 使用上下锚点自动调整大小http dl dropbox com u 11270323 stackoverflow autosize ma
  • 登录系统生成的PHP空会话文件

    最近我注意到正在创建许多空白会话 我不确定为什么 但我相信我正在以正确的方式做所有事情 目前我们创建一个会话 当用户logs in or 寄存器 然后我们检查用户是否使用isset COOKIE auth 属于登录或注册期间创建的会话 如果
  • 按值分组 RAND()

    有可能获得组的随机值吗 nID val A XXX A YYY B L B M B N B P 使用此 SQL SELECT nID VAL FROM T1 GROUP BY nID 我的结果总是 nID val A XXX B L 但我想
  • 如何在使用自定义窗口镶边时向 WPF 标题栏添加按钮?

    我正在尝试创建一个简单的按钮模板 其中按钮通常看起来像一条水平线 但是当鼠标悬停在按钮上时 按钮后面会显示一个 矩形 颜色填充 这是我的代码 但我似乎无法触发触发器
  • 如何让子窗口保持在最上面?

    我在用window open从父窗口打开子窗口 我希望子窗口保持在顶部 以便用户在父窗口中进行输入时可以参考它 这可以做到吗 我目前使用的是 Firefox 但如果它能在所有浏览器中运行 那就太好了 怎么样使用一个弹出div而不是打开一个新
  • GitHub Packages Docker - 拉取映像配置时出错:未知 blob

    GitHub 包开始返回error pulling image configuration unknown blob这个周末尝试拉取 docker 镜像时 它仍然可以将图像推送到注册表 我在 GitHub 上没有找到任何指向问题的信息 00
  • MySQL 与实体框架 - 我做错了什么?

    我对实体框架甚至 ADO NET 完全陌生 通常不会对数据库做太多工作 我下载并安装了MySQL 连接器 NET 6 3 5 我在 Visual Studio 2010 中创建了一个新的 C 项目 我向我的项目添加了一个新的 ADO NET
  • 使用 Facebook4j api 从页面获取帖子

    我想知道是否有一种方法可以使用 Facebook4J API 从 Facebook 页面获取所有 甚至最近 帖子 我知道可以从用户的墙或提要中获取所有帖子 但我在 API 或文档中找不到任何显示如何从页面获取帖子的内容 看着http fac
  • 如何检查bash脚本中特定目录中是否存在文件?

    这是我一直在尝试的 但没有成功 如果我想检查 example 目录中是否存在文件 FILE 1 if e FILE example then echo File exists else echo File does not exist fi
  • 迭代字典时如何避免 swift 中的重复键错误

    我正在练习 swift 我正在尝试迭代字典来打印密钥 但它给了我一个 致命错误 字典文字包含重复的键 如何消除错误 let people age 14 age 15 age 75 age 43 age 103 age 87 age 12 f
  • 获取 svg 路径上点的 y 坐标

    我想我需要添加一些解释 我想问这个问题 因为太短的问题不符合质量标准 有趣 所以 问题是 如何获取 svg 路径上特定 x 坐标处点的 y 坐 标 这并不简单 因为一条路径可能有多个具有指定的点x协调 SVG DOM 中没有内置函数可以执行
  • 提取 DOCX 注释

    我是一名教师 我想要一份对我布置的论文发表评论的所有学 生以及他们所说内容的列表 Drive API 的东西对我来说太具有挑战性 但我想我可以将它们下载为 zip 并解析 XML 评论被标记为w comment标签 与w t对于评论文本和
  • 在视图中自动布局“打开”时无法移动 UILabel 的 Y 位置

    我一定在这里遗漏了一些非常明显的东西 但这是一个让我沮丧了好几天的问题 在 xcode 4 5 上的 iOS 项目中 我在 XIB 中有几个标签 一个在另一个之上 在一个UIScrollView占据一个UIView 每个标签与视图一样宽 并
  • 在Python循环中使用迭代器作为变量名

    我一直想知道是否有一种方法可以在 Python 循环中使用迭代器作为变量名 例如 如果我想创建对象v0 v1 v2 有没有办法做这样的事情 for i in range 3 v str i i 2 我知道语法是错误的 但想法应该很清楚 相当
  • 编译32位时出现__int128错误

    c random h 106 error expected unqualified id before int128 当我编译 32 位程序时 上面是我得到的错误 我在用着http sourceforge net projects ming
  • 使用Scala宏生成方法

    我想在 Scala 2 11 中使用注释宏生成方法的别名 我什至不确定这是否可能 如果是 怎么办 示例 鉴于下面的内容 我希望注释宏扩展到 class Socket alias aliases Seq ask read def load n