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

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

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

Например, с учетом следующего кода (из Scala By Example pages 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? Я заказал книгу, но она еще не прибыла.

Ответ 1

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

Ответ 2

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

Ответ 3

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