1

ВЯвляется ли Scala `match` конструктивным синтаксическим сахаром? Если да, то как это работает?

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

object CaseExample { 
    def eval(e: Expr): Int = e match { 
    case Number(n) => n 
    case Sum(e1, e2) => eval(e1) + eval(e2) 
    } 
    def main(args: Array[String]) { 
    println(eval(Sum(Number(1), Number(2))))  //> 3 
    } 
} 

там совсем немного синтаксического сахара происходит. Я понимаю, что case неявно создает два объекта

object Number extends Expr { 
    def apply(n: Int) = new Number(n) 
} 
object Sum extends Expr { 
    def apply(e1: Expr, e2: Expr) = new Sum(e1, e2) 
} 

, и именно поэтому мы можем написать, например, Sum(...) и все еще создайте объект через класс, так как Sum(...) также является синтаксическим сахаром для Sum.apply(...).

Я прав, что match Конструкция также синтаксический сахар? Если да, то каким образом - например. case Number(n) - переписан компилятором?

Я спрашиваю, потому что я не вижу, чтобы n в case Number(n) где-нибудь определен и/или привязан к значению. Как ни странно, в конструкции match имеет место случай первой буквы (если она будет в верхнем регистре, она будет постоянной). Это странно, потому что, насколько я знаю, это только в конструкции релевантности match, поэтому я понятия не имею, как это было бы de-sugared.

+0

Я не знаю, является ли совпадение шаблонов _actually_ desugared, но в принципе оно может быть преобразовано в некоторые 'if's, используя [' unapply'] (http://stackoverflow.com/a/36860003/1346276). – phg

ответ

5

Да, match - синтаксический сахар. Он вызывает метод unapply на вашем объекте. У Даниэля Уэстхиде есть nice blog post about it.

В частности, при определении вашего case class для Number, вот что фактически генерирует компилятор:

case class Number(n: scala.Int) extends scala.AnyRef with Expr with scala.Product with scala.Serializable { 
    val n: scala.Int = { /* compiled code */ } 
    /* omitted for brevity */ 
} 
object Number extends scala.runtime.AbstractFunction1[scala.Int, Number] with scala.Serializable { 
    def this() = { /* compiled code */ } 
    final override def toString(): java.lang.String = { /* compiled code */ } 
    def apply(n: scala.Int): Number = { /* compiled code */ } 
    def unapply(x$0: Number): scala.Option[scala.Int] = { /* compiled code */ } 
} 

Как вы можете видеть, объект Number спутник поставляется с unapply методом сгенерированного компилятором.

Подробное описание конструкции объекта Scala для соответствия шаблону here.

- EDIT -

Если вы хотите, чтобы увидеть фактический, компилятор сгенерированный код, запустите scalac -print Number.scala. Вот соответствующие биты:

<synthetic> object Number extends scala.runtime.AbstractFunction1 with Serializable { 
    /* ... */ 
    case <synthetic> def unapply(x$0: Number): Option = if (x$0.==(null)) 
    scala.this.None 
    else 
    new Some(scala.Int.box(x$0.n())); 
    /* ... */ 
} 

Если вы пишете выражение match, вы можете так же запустить scalac -print, чтобы увидеть, как сама match является обессахаренными (в основном: if и else выражения).

+0

Можете ли вы объяснить: «Number (n) => n' a scala function и« match »-классы, считанные как« case »(« Number (n) => n'), или должны быть прочитаны как (case Number (n) ') (' => ') (' n')? Или даже по-другому? – Make42

+0

Выражения вида 'e match {case p1 => f1; ...; case pn => fn} 'похожи на магические выражения' switch'. Если 'e'_is a_' p1', тогда запустите функцию 'p1 => f1', иначе, если' e' _is a_ 'p1 + 1', запустите функцию' p1 + 1 => f1 + 1' и т. Д. , На практике вам не часто нужно знать, как они работают _actually_ (и я сам не мог сказать вам правила). Я могу сказать вам, что в вашем примере 'match' выше компилятор генерирует байт-код, который проверяет тип' e' на каждый шаблон (во время выполнения, например, 'Number'), используя' instanceof', а затем выполняет код справа сторона. –

+0

Напишите свой комментарий о «Я не вижу, что' n' в 'случае Number (n)' где-нибудь определен и/или привязан к значению ", вот как я думаю об этом:' Number (n) 'является «Разрушитель». Скала называет их «экстракторами». Если 'e' оказывается' Number', то 'n' будет привязано к любому значению, определяемому экстрактором. Поскольку вы использовали класс case, этот экстрактор ('unapply') автоматически генерируется для вас и в основном является обратным конструктором. Таким образом, 'n' в выражении с правой стороны привязывается к значению' n' в 'case class Number'. Имеют смысл? –

Смежные вопросы