Scala 中的模式匹配是如何在字节码级别实现的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/754166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:25:17  来源:igfitidea点击:

How is pattern matching in Scala implemented at the bytecode level?

performancescalapattern-matchingbytecode

提问by Esko Luontola

How is pattern matching in Scala implemented at the bytecode level?

Scala 中的模式匹配是如何在字节码级别实现的?

Is it like a series of if (x instanceof Foo)constructs, or something else? What are its performance implications?

它像一系列的if (x instanceof Foo)结构,还是其他什么?它的性能影响是什么?

For example, given the following code (from Scala By Examplepages 46-48), how would the equivalent Java code for the evalmethod look like?

例如,给定以下代码(来自Scala By Example第 46-48 页),该方法的等效 Java 代码将是eval什么样的?

abstract class Expr
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

def eval(e: Expr): Int = e match {
  case Number(x) => x
  case Sum(l, r) => eval(l) + eval(r)
}

P.S. I can read Java bytecode, so a bytecode representation would be good enough for me, but probably it would be better for the other readers to know how it would look like as Java code.

PS 我可以阅读 Java 字节码,所以字节码表示对我来说已经足够了,但对于其他读者来说,了解它作为 Java 代码的样子可能会更好。

P.P.S. Does the book Programming in Scalagive an answer to this and similar questions about how Scala is implemented? I have ordered the book, but it has not yet arrived.

PPS Scala 编程这本书是否回答了这个问题以及关于 Scala 是如何实现的类似问题?我已经订购了这本书,但还没有到。

采纳答案by James Iry

The low level can be explored with a disassembler but the short answer is that it's a bunch of if/elses where the predicate depends on the pattern

可以使用反汇编器探索低级别,但简短的回答是它是一堆 if/else,其中谓词取决于模式

case Sum(l,r) // instance of check followed by fetching the two arguments and assigning to two variables l and r but see below about custom extractors 
case "hello" // equality check
case _ : Foo // instance of check
case x => // assignment to a fresh variable
case _ => // do nothing, this is the tail else on the if/else

There's much more that you can do with patterns like or patterns and combinations like "case Foo(45, x)", but generally those are just logical extensions of what I just described. Patterns can also have guards, which are additional constraints on the predicates. There are also cases where the compiler can optimize pattern matching, e.g when there's some overlap between cases it might coalesce things a bit. Advanced patterns and optimization are an active area of work in the compiler, so don't be surprised if the byte code improves substantially over these basic rules in current and future versions of Scala.

您可以使用诸如“case Foo(45, x)”之类的模式或模式和组合做更多的事情,但通常这些只是我刚刚描述的内容的逻辑扩展。模式也可以有守卫,这是对谓词的额外约束。也有编译器可以优化模式匹配的情况,例如,当情况之间有一些重叠时,它可能会合并一些东西。高级模式和优化是编译器中的一个活跃工作领域,因此如果字节码在当前和未来版本的 Scala 中比这些基本规则有显着改进,请不要感到惊讶。

In addition to all that, you can write your own custom extractors in addition to or instead of the default ones Scala uses for case classes. If you do, then the cost of the pattern match is the cost of whatever the extractor does. A good overview is found in http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

除此之外,您可以编写自己的自定义提取器,以补充或代替 Scala 用于案例类的默认提取器。如果这样做,那么模式匹配的成本就是提取器所做的任何事情的成本。在http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf 中有一个很好的概述

回答by Jorge Ortiz

James (above) said it best. However, if you're curious it's always a good exercise to look at the disassembled bytecode. You can also invoke scalacwith the -printoption, which will print your program with all Scala-specific features removed. It's basically Java in Scala's clothing. Here's the relevant scalac -printoutput for the code snippet you gave:

詹姆斯(上)说得最好。但是,如果您很好奇,查看反汇编的字节码总是一个很好的练习。您还可以scalac使用该-print选项进行调用,该选项将打印您的程序,并删除所有特定于 Scala 的功能。它基本上是披着 Scala 外衣的 Java。这是scalac -print您提供的代码片段的相关输出:

def eval(e: Expr): Int = {
  <synthetic> val temp10: Expr = e;
  if (temp10.$isInstanceOf[Number]())
    temp10.$asInstanceOf[Number]().n()
  else
    if (temp10.$isInstanceOf[Sum]())
      {
        <synthetic> val temp13: Sum = temp10.$asInstanceOf[Sum]();
        Main.this.eval(temp13.e1()).+(Main.this.eval(temp13.e2()))
      }
    else
      throw new MatchError(temp10)
};

回答by om-nom-nom

Since version 2.8, Scala has had the @switchannotation. The goal is to ensure, that pattern matching will be compiled into tableswitch or lookupswitchinstead of series of conditional ifstatements.

从 2.8 版开始,Scala 有了@switch注释。目标是确保模式匹配将被编译为tableswitch 或 lookupswitch而不是一系列条件if语句。