Как сопоставление с образцом в Scala реализовано на уровне байт-кода? - PullRequest
117 голосов
/ 16 апреля 2009

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

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

Например, учитывая следующий код (из 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. Я могу читать байт-код Java, поэтому представление байт-кода было бы достаточно для меня, но, возможно, для других читателей было бы лучше знать, как это будет выглядеть как код Java.

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

Ответы [ 3 ]

92 голосов
/ 16 апреля 2009

Низкий уровень можно исследовать с помощью дизассемблера, но краткий ответ заключается в том, что это набор элементов 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

74 голосов
/ 16 апреля 2009

Джеймс (выше) сказал это лучше всего. Однако, если вам интересно, всегда полезно посмотреть на разобранный байт-код. Вы также можете вызвать scalac с параметром -print, который напечатает вашу программу со всеми удаленными функциями, специфичными для 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)
};
31 голосов
/ 14 августа 2012

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

...