Почему отсутствие блока else преобразуется в тип Unit для функции? - PullRequest
0 голосов
/ 06 октября 2019

Я заметил, что в строке else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc} обнаружено несоответствие типов. Потому что в моем if ... else if ...

def euclidianDivision(dividend:Int,divisor:Int):(Int,Int)={
  val quotient = dividend/divisor
  val remainder = dividend%divisor

  (quotient,remainder)
}
def firstExpansion(dividend:Int,divisors:List[Int]):List[(Int,Int)]={
  def firstExpansionIter(dividend:Int,divisors:List[Int], acc:List[(Int,Int)]):List[(Int,Int)]= {
    val div1:Int = divisors.head
    val (q1,r1):(Int,Int) = euclidianDivision(dividend,div1)
    val newAcc:List[(Int,Int)] = acc:::List((div1,q1))
    if (divisors.tail.contains(r1)){
      firstExpansionIter(r1,divisors.tail,newAcc)
    }else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc}
  }
  firstExpansionIter(dividend,divisors,List((0,0))).tail
}

нет условия else. Вот код ошибки:

Ошибка: (32, 15) несоответствие типов;найдено: требуемая единица: список [(Int, Int)]} иначе, если (r1 == 0 || divisors.tail.isEmpty ||! divisors.tail.contains (r1)) {newAcc}

Я могу исправить это, добавив предложение else, но как получится, если по умолчанию результат не обрабатывается, функция пытается вернуть Unit?

Примечание: исправленный код:

def firstExpansion(dividend:Int,divisors:List[Int]):List[(Int,Int)]={
  def firstExpansionIter(dividend:Int,divisors:List[Int], acc:List[(Int,Int)]):List[(Int,Int)]= {
    val div1:Int = divisors.head
    val (q1,r1):(Int,Int) = euclidianDivision(dividend,div1)
    val newAcc:List[(Int,Int)] = acc:::List((div1,q1))
    if (divisors.tail.contains(r1)){
      firstExpansionIter(r1,divisors.tail,newAcc)
    }else if(r1 == 0 || divisors.tail.isEmpty || !divisors.tail.contains(r1)){newAcc}
    else throw new RuntimeException("Something unexpected happened.")
  }
  firstExpansionIter(dividend,divisors,List((0,0))).tail
}

1 Ответ

4 голосов
/ 06 октября 2019

Я могу исправить это, добавив предложение 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 есть.)

...