Как совпадающее слово опущено в Scala?

В Scala вы можете сделать

list.filter { item =>
    item match {
      case Some(foo) => foo.bar > 0
    }
}

Но вы также можете сделать более быстрый путь, опуская match:

list.filter {
  case Some(foo) => foo.bar > 0
}

Как это поддерживается в Scala? Это новое в 2,9? Я искал его, и я могу понять, что делает это возможным. Это просто часть компилятора Scala?

Ответ 1

спецификация языка описывает это в разделе 8.5. Соответствующие части:

Анонимная функция может быть определена последовательностью случаев

{ case p1 => b1 ... case pn => bn }

Если ожидаемый тип scala.Functionk[S1, ..., Sk, R], выражение берется эквивалентны анонимной функции:

(x1 : S1, ..., xk : Sk) => (x1, ..., xk) match {
  case p1 => b1 ... case pn => bn
}

Если ожидаемый тип scala.PartialFunction[S, R], выражение берется эквивалентны следующему выражению создания экземпляра:

new scala.PartialFunction[S, T ] {
  def apply(x: S): T = x match {
    case p1 => b1 ... case pn => bn
  }
  def isDefinedAt(x: S): Boolean = {
    case p1 => true ... case pn => true
    case _ => false
  }
}  

Таким образом, ввод выражения как PartialFunction или Function влияет на компиляцию выражения.

Также trait PartialFunction [-A, +B] extends (A) ⇒ B, поэтому частичная функция PartialFunction[A,B] также является Function[A,B].

Ответ 2

Изменить: части этого ответа неверны; обратитесь к huynhjl answer.


Если вы опускаете match, вы сигнализируете компилятору, что вы определяете частичную функцию. Частичная функция - это функция, которая не определена для каждого входного значения. Например, функция фильтра определена только для значений типа Some[A] (для вашего пользовательского типа A).

PartialFunction бросьте MatchError, когда вы пытаетесь применить их там, где они не определены. Поэтому вы должны убедиться, что когда вы передаете PartialFunction, где определен обычный Function, ваша частичная функция никогда не будет вызываться с помощью unhanded аргумента. Такой механизм очень полезен, например, для распаковки кортежей в коллекции:

val tupleSeq: Seq[(Int, Int)] = // ...
val sums = tupleSeq.map { case (i1, i2) => i1 + i2 }

API, которые запрашивают частичную функцию, например, как collect фильтр-подобную операцию в коллекциях, обычно вызывают isDefinedAt перед применением частичной функции. Там безопасно (и часто требуется) иметь частичную функцию, которая не определена для каждого входного значения.

Итак, вы видите, что хотя синтаксис близок к синтаксису match, это совсем другое дело, с которым мы имеем дело.

Ответ 3

- Пересмотренный пост -

Хмм, я не уверен, что вижу разницу, Scala 2.9.1.RC3,

val f: PartialFunction[Int, Int] = { case 2 => 3 }
f.isDefinedAt(1) // evaluates to false
f.isDefinedAt(2) // evaluates to true
f(1) // match error

val g: PartialFunction[Int, Int] = x => x match { case 2 => 3 }
g.isDefinedAt(1) // evaluates to false
g.isDefinedAt(2) // evaluates to true
g(1) // match error

Кажется, что f и g ведут себя точно так же, как PartialFunctions.

Вот еще один пример, демонстрирующий эквивалентность:

Seq(1, "a").collect(x => x match { case s: String => s }) // evaluates to Seq(a)

Еще интереснее:

// this compiles
val g: PartialFunction[Int, Int] = (x: Int) => {x match { case 2 => 3 }}

// this fails; found Function[Int, Int], required PartialFunction[Int, Int]
val g: PartialFunction[Int, Int] = (x: Int) => {(); x match { case 2 => 3 }}

Итак, на уровне компилятора есть специальная оболочка для преобразования между x => x match {...} и просто {...}.

Update. После прочтения спецификации языка это кажется ошибкой для меня. Я отправил SI-4940 в трекер ошибок.