Я могу исправить это, добавив предложение else
, но как получится, если по умолчанию не обрабатывается результат, функция пытается вернуть Unit
?
InScala, в отличие от более «императивных» языков, (почти) все является выражением (существует очень мало операторов), и каждое выражение оценивается как значение (что также означает, что каждый метод возвращает значение).
Этоозначает, что, например, условное выражение if (condition) consequence else differentConsequence
является выражением, которое оценивается как значение.
Например, в этом фрагменте кода:
val foo = if (someRandomCondition) 42 else "Hello"
часть then
выражения будет иметь значение 42
, часть выражения else
будет иметь значение "Hello"
, что означает, что выражение if
в целом будет иметь значение 42
или "Hello"
.
Итак, какой будет тип foo
? Ну, в случае then
значение имеет тип Int
, а в случае else
значение имеет тип String
. Но это зависит от значения времени выполнения , равного someRandomCondition
, которое неизвестно во время компиляции. Таким образом, единственный выбор, который мы имеем в качестве типа для выражения целом if
, - это наименьший общий предок (технически, слабая наименьшая верхняя граница )Int
и String
, то есть Any
.
В языке с типами объединения мы могли бы дать ему более точный тип, а именно тип объединения Int | String
. ( Scala 3 имеет типы объединения , поэтому мы могли бы дать выражению этот точный тип, хотя Scala 3 не будет выводить типы объединения.) В Scala 3 мы могли бы даже аннотировать его еще более точным типом 42 | "Hello"
, который на самом деле является типом, который TypeScript собирается вывести для эквивалентного условного выражения:
const foo = someRandomCondition ? 42 : "Hello"
Теперь давайте перейдем к коду в вопросе:
val bar = if (someRandomCondition) 42
Чтобудет тип bar
будет? Выше мы говорили, что это самый низкий общий предок среди типов ветвей then
и else
, но ... каков тип ветви else
? Что вычисляет ветвь else
?
Помните, мы говорили, что каждое выражение оценивается в значение , поэтому ветвь else
должна вычислять для некоторыхстоимость. Он не может просто быть оценен как «ничто».
Это решается так называемым значением единицы типа единицы . Значение и тип единицы называются значением и типом «единицы», потому что тип спроектирован таким образом, что он может быть заселен только одним значением . Тип модуля не имеет ни членов, ни свойств, ни полей, ни семантики, ни ничего. Таким образом, невозможно отличить два значения типа блока друг от друга или поставить другое значение: может существовать только одно значение типа блока, потому что совсем другое значение типа блока должно бытьидентичны.
Во многих языках программирования значение и тип единицы используют те же обозначения, что и значение и тип кортежа, и просто идентифицируются пустым кортежем ()
. Пустой кортеж и значение единицы - это одно и то же: они не имеют никакого содержания, никакого значения. Например, в Haskell тип и значение записываются как ()
.
. В Scala также есть единичное значение, а также записывается ()
. Однако типом единицы измерения является scala.Unit
.
Итак, значение единицы, которое является бесполезным значением, используется для обозначения бессмысленного возвращаемого значения.
Связанное, но другое понятиев некоторых императивных языках тип void
(или в некоторых языках это скорее «псевдотип»).
Обратите внимание, что «ничего не возвращает» отличается от «не возвращает», чтостанет важным во второй части этого ответа.
Итак, первая часть головоломки: спецификация языка Scala гласит, что
if (condition) expression
эквивалентно
if (condition) expression else ()
Это означает, что в случае (неявного) else
тип возвращаемого значения равен Unit
, что несовместимо с List[(Int, Int)]
, и, следовательно, вы получаете ошибку типа.
Но почемули исключение исправить это?
Это приводит нас к второму специальному типу: Nothing
. Nothing
- это так называемый нижний тип , что означает, что это подтип любого типа . Nothing
не имеет не никакого значения. Итак, что же тогда будет означать тип возврата Nothing
?
Это означает выражение, которое не возвращает . И я повторяю то, что я сказал выше: это отличается от возврата ничего.
Метод, который имеет только побочный эффект, ничего не возвращает, но он возвращает . Тип возвращаемого значения - Unit
, а возвращаемое значение - ()
. Он не имеет значимого возвращаемого значения.
Метод, который имеет бесконечный цикл или генерирует исключение вообще не возвращает . Его тип возвращаемого значения Nothing
и , он не имеет возвращаемого значения .
. Поэтому, исключение в предложении else
решает проблему: это означает, чтотип предложения else
равен Nothing
, а поскольку Nothing
является подтипом каждого типа , даже не имеет значения какой тип then
предложение является наименьшим общим супертипом типа предложения then
, а Nothing
будет всегда быть типом предложения then
. (Подумайте об этом: самый низкий общий предок отца и любого из его детей, внуков, правнуков и т. Д. Всегда будет сам отцом. Самый низкий общий предок T
и любой подтип T
всегда будутbe T
. Так как Nothing
является подтипом всех типов, самый низкий общий предок T
и Nothing
всегда будет T
, потому что Nothing
всегда является подтипом T
, независимо от того, чтоT
есть.)