Как совпадающее слово опущено в 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 в трекер ошибок.