提取器宏

语言
此文档页面特定于 Scala 2 中发布的功能,这些功能已在 Scala 3 中删除或被替代功能所取代。除非另有说明,此页面中的所有代码示例均假定你使用的是 Scala 2。

实验性

Eugene Burmako

提取器宏是 Scala 2.11.x 和 Scala 2.12.x 的一项功能,由 Paul Phillips 在 Scala 2.11.0-M5 中引入的基于名称的提取器启用。Scala 2.10.x 中不支持提取器宏。它们也不受 Scala 2.10.x 的宏天堂支持。

模式

简而言之,给定一个 unapply 方法(为简单起见,在此示例中,被检查对象是具体类型,但也可以让提取器是多态的,如测试中所示)

def unapply(x: SomeType) = ???

可以编写一个宏,该宏根据每次调用(c.prefix)的目标和被检查对象的类型(x)为 unapply 生成提取签名,然后将这些签名传达给类型检查器。

例如,以下是如何定义一个宏,该宏仅将被检查对象传回模式匹配(有关如何表示涉及多个提取对象的签名的信息,请访问 scala/scala#2848)。

def unapply(x: SomeType) = macro impl
def impl(c: Context)(x: c.Tree) = {
  q"""
    new {
      class Match(x: SomeType) {
        def isEmpty = false
        def get = x
      }
      def unapply(x: SomeType) = new Match(x)
    }.unapply($x)
  """
}

除了实现特定于域的匹配逻辑的匹配器之外,这里还有相当多的样板,但它的每一部分看起来对于安排与类型检查器的非令人沮丧的对话都是必要的。也许可以在这个部门做得更好,但我不知道如何做到,除非对类型检查器进行修改。

即使模式使用结构类型,但不知何故没有生成任何反射调用(通过 -Xlog-reflective-calls 验证,然后通过手动检查生成代码进行验证)。这对我来说是个谜,但这也是个好消息,因为这意味着提取器宏不会导致性能下降。

几乎。不幸的是,我无法将匹配器转换为值类,因为无法声明本地值类。不过,我留了一个金丝雀(neg/t5903e),一旦该限制解除,它将让我们知道。

用例

特别是,该模式可用于为字符串插值器实现变形模式匹配器,而无需诉诸肮脏技巧。例如,准引号未应用现在可以取消硬编码

def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], ...) = {
  ...
  fun.tpe match {
    case ExtractorType(unapply) if mode.inPatternMode =>
      // this hardcode in Typers.scala is no longer necessary
      if (unapply == QuasiquoteClass_api_unapply) macroExpandUnapply(...)
      else doTypedUnapply(tree, fun0, fun, args, mode, pt)
  }
}

此处的大致实现策略包括编写一个析取宏,它解构c.prefix,分析StringContext的部分,然后生成如上所述的适当匹配器。

run/t5903arun/t5903brun/t5903crun/t5903d查看我们的测试用例,了解此用例和其他析取宏用例的实现。

黑盒与白盒

析取宏必须是白盒。如果您将析取宏声明为黑盒,它将不起作用。

此页面的贡献者