Какова причина добавления ключевого слова case к Scala?

Помимо:

case class A

... случай, который весьма полезен?

Зачем нам нужно использовать case в match? Не было бы:

x match {
  y if y > 0 => y * 2
  _ => -1
}

... be много красивее и кратким?

Или почему нам нужно использовать case, когда функция берет кортеж? Скажем, мы имеем:

val z = List((1, -1), (2, -2), (3, -3)).zipWithIndex

Теперь нет:

z map { case ((a, b), i) => a + b + i }

... путь более уродливый, чем просто:

z map (((a, b), i) => a + b + i)

...

Ответ 1

Вторая проблема, анонимные функции, которые избегают case, является дебатом:

https://groups.google.com/d/msg/scala-debate/Q0CTZNOekWk/z1eg3dTkCXoJ

Также: http://www.scala-lang.org/old/node/1260

Для первой проблемы выбор заключается в том, разрешаете ли вы блок или выражение в RHS стрелки.

На практике я нахожу, что более короткие корпуса тела обычно предпочтительнее, поэтому я могу, конечно, представить ваш альтернативный синтаксис, создавая более четкий код.

Рассмотрим однострочные методы. Вы пишете:

def f(x: Int) = 2 * x

то вам нужно добавить инструкцию. Я не знаю, может ли IDE автоматически добавлять парсеры.

def f(x: Int) = { val res = 2*x ; res }

Это кажется не хуже, чем требование того же синтаксиса для корпусов тела.

Для просмотра предложения case case Pattern Guard => body.

В настоящее время body - это блок или последовательность операторов и выражение результата.

Если body было выражением, вам понадобится скобки для нескольких операторов, например, функция.

Я не думаю, что => приводит к двусмысленности, поскольку литералы функций не квалифицируются как шаблоны, в отличие от литералов типа 1 или "foo".

Одна загвоздка может быть: { case foo => ??? } - "анонимная функция соответствия шаблону" (SLS 8.5). Очевидно, что если случай является необязательным или исключается, то { foo => ??? } является неоднозначным. Вам нужно будет отличить case case для anon funs (где требуется case) и case case в match.

Один встречный аргумент для текущего синтаксиса заключается в том, что в интуиции, происходящей из C, вы всегда втайне надеетесь, что ваше совпадение будет скомпилировано в таблицу переключателей. В этой метафоре случаются ярлыки для перехода к метке, а метка - это только адрес последовательности операторов.

Альтернативный синтаксис может способствовать более наглядному подходу:

x match {
  C => c(x)
  D => d(x)
  _ => ???
}
@inline def c(x: X) = ???
//etc

В этой форме он больше похож на таблицу диспетчеризации, а тело соответствия ссылается на синтаксис Map, Map(a -> 1, b -> 2), то есть на простое упрощение ассоциации.

Ответ 2

Во-первых, как мы знаем, можно поставить несколько операторов для одного и того же сценария, не требуя какой-либо разделительной записи, просто скачок линии, например:

x match {
       case y if y > 0 => y * 2
                          println("test")
                          println("test2")  // these 3 statements belong to the same "case"
}

Если case не понадобилось, компилятор должен был бы найти способ узнать, когда речь идет о следующем сценарии.

Например:

x match {
   y if y > 0 => y * 2
   _ => -1
}

Как компилятор узнает, принадлежит ли _ => -1 к первому сценарию или представляет следующий случай?

Кроме того, как компилятор знал бы, что знак => не представляет собой литеральную функцию, а фактический код для текущего case?

Компилятору наверняка понадобится такой код, как это позволяет изоляция случаев: (используя фигурные скобки или что-то еще)

x match {
    {y if y > 0 => y * 2}
    {_ => -1}  // confusing with literal function notation
}

И, конечно, решение (предоставляемое в настоящее время scala) с использованием ключевого слова case намного читабельнее и понятнее, чем использование некоторого способа разделения, например, фигурных скобок в моем примере.

Ответ 3

Добавление в ответ @Mik378:

Когда вы пишете это: (a, b) => something, вы определяете анонимную Function2 - функцию, которая принимает два параметра.

Когда вы пишете это: case (a, b) => something, вы определяете анонимный PartialFunction, который принимает один параметр и сопоставляет его с парой.

Итак, вам нужно использовать ключевое слово case, чтобы различать эти два.

Ответ 4

Одним из ключевых аспектов читаемости кода являются слова, которые привлекают ваше внимание. Например, return привлекает ваше внимание, когда вы его видите, потому что вы знаете, что это такое решающее действие (выход из функции и возможность отправки значения обратно вызывающему абоненту).

Другим примером является break - не то, что мне нравится break, но оно привлекает ваше внимание.

Я бы согласился с @Mik378, что case в Scala более читаем, чем альтернативы. Помимо замешательства компилятора, который он упоминает, он привлекает ваше внимание.

Я все для краткого кода, но есть строка между кратким и неразборчивым. Я с радостью сделаю сделку с 4n символами (где n - количество случаев) для существенной читаемости, которую я получаю взамен.