Как сопоставление с образцом в Scala реализовано на уровне байт-кода?

123

Как сопоставление с образцом в Scala реализовано на уровне байт-кода?

Это что-то вроде серии if (x instanceof Foo)конструкций или чего-то еще? Каковы его последствия для производительности?

Например, учитывая следующий код (из Scala By Example, страницы 46-48), как будет evalвыглядеть эквивалентный Java-код для метода?

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)
}

PS Я могу читать байт-код Java, поэтому мне было бы достаточно представления байт-кода, но, вероятно, другим читателям было бы лучше знать, как он будет выглядеть как Java-код.

PPS Дает ли книга « Программирование на Scala» ответ на этот и подобные вопросы о том, как реализована Scala? Я заказал книгу, но она еще не пришла.

Эско Луонтола
источник
Почему бы вам просто не скомпилировать пример и не разобрать его с помощью дизассемблера байт-кода Java?
Zifre
Я, наверное, так и сделаю, если только кто-то сначала не ответит хорошо. Но сейчас я хочу немного поспать. ;)
Эско Луонтола
27
Вопрос будет полезен другим читателям!
djondal
1
@djondal: лучший способ сказать это - просто проголосовать за вопрос :-)
Blaisorblade

Ответы:

96

Низкий уровень можно изучить с помощью дизассемблера, но краткий ответ заключается в том, что это набор if / elses, где предикат зависит от шаблона.

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

Вы можете сделать гораздо больше с помощью шаблонов, таких как или шаблоны и комбинации, такие как case Foo (45, x), но в целом это просто логические расширения того, что я только что описал. У шаблонов также могут быть охранники, которые являются дополнительными ограничениями для предикатов. Есть также случаи, когда компилятор может оптимизировать сопоставление с образцом, например, когда есть некоторое перекрытие между случаями, он может немного объединить вещи. Расширенные шаблоны и оптимизация являются активной областью работы в компиляторе, поэтому не удивляйтесь, если байтовый код существенно улучшится по сравнению с этими базовыми правилами в текущей и будущих версиях Scala.

В дополнение ко всему, вы можете написать свои собственные экстракторы в дополнение к тем, которые Scala использует по умолчанию для классов case, или вместо них. Если вы это сделаете, то стоимость сопоставления с образцом будет равна стоимости того, что делает экстрактор. Хороший обзор можно найти в http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

Джеймс Айри
источник
Я считаю, что это текущая ссылка: infoscience.epfl.ch/record/98468/files/…
greenoldman
78

Джеймс (вверху) сказал это лучше всего. Однако, если вам интересно, всегда полезно посмотреть на дизассемблированный байт-код. Вы также можете вызвать scalacэту -printопцию, которая распечатает вашу программу без всех специфичных для Scala функций. По сути, это Java в одежде Scala. Вот соответствующий 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)
};
Хорхе Ортис
источник
34

Начиная с версии 2.8 в Scala есть аннотация @switch . Цель состоит в том, чтобы гарантировать, что сопоставление шаблонов будет скомпилировано в tablewitch или lookupswitch, а не в серию условных ifоператоров.

ом Ном ном
источник
6
когда выбрать @switch вместо обычного, если еще?
Aravind Yarram
2
использование @switchболее эффективно, чем обычное сопоставление с образцом. поэтому, если все случаи содержат постоянные значения, вы всегда должны использовать @switch(потому что реализация байт-кода будет такой же, как в java, switchа не во многих if-else)
лев