Как получить сигнатуру типа для согласования с компилятором scala при извлечении значения из Option [A]? - PullRequest
0 голосов
/ 04 марта 2019

Допустим, у меня есть переменная x, которая имеет тип Option[Double].Я хочу получить это двойное значение из переменной, поэтому я подозреваю, что решением будет x.getOrElse(None).

Сигнатура типа getOrElse выглядит следующим образом:

def getOrElse[B >: A](default: => B): B

None - это просто Option[Nothing].Если я напишу следующее:

def mean(xs: Seq[Double]): Option[Double] =
   if (xs.isEmpty) None
   else Some(xs.sum / xs.length)

val avg = mean(xs) getOrElse(None) // compiles
val theAvg: Double = mean(xs) getOrElse(None) // doesn't compile

Что здесь происходит с типами?REPL говорит мне (используя: t), что avg имеет тип Any.Как это может быть?Почему тип соответствует в первую очередь, когда None имеет тип Option[Nothing]?

Что я не понимаю о типах здесь?

Ответы [ 3 ]

0 голосов
/ 04 марта 2019

Как это может быть?

Предполагаемый тип Any здесь является результатом того, что компилятор пытается найти общего предка Double и Option[Nothing].Первый общий предок для обоих этих типов - Any, и поэтому компилятор выводит это.

Почему тип совпадает в первую очередь, когда None имеет тип Option[Nothing]

Отличный вопрос.Давайте проверим подпись для getOrElse:

final def getOrElse[B >: A](default: => B): B

Давайте увеличим ограничение B >: A, что это значит?Это означает, что мы можем указать любой тип B, который является супертипом из A.Почему мы хотим, чтобы это был супер тип A для начала?Почему бы не A сам?Если параметр типа заставит нас использовать A здесь, мы будем ограничены Double, и ваш пример не будет компилироваться.Чтобы ответить на этот вопрос, нам нужно взглянуть на определение Option[A]:

sealed abstract class Option[+A]

Мы видим, что A имеет + рядом с ним.Этот плюс указывает на наличие ковариации.Короче говоря, ковариация позволяет нам сохранять «отношение подтипа» между типами, которые сами определены внутри других «контейнерных» типов, сохраняя следующее отношение:

A <: B <=> Option[A] <: Option[B]

Что означает, что A являетсяподтип B, мы можем рассматривать Option[A] как подтип Option[B].Почему это важно и как это связано с сигнатурой метода getOrElse?Ну, ковариация накладывает ограничения на параметры типа!Параметры ковариантного типа нельзя использовать в качестве входных параметров или в позиции входного параметра (позиция является контравариантной, если она встречается при нечетном количестве конструкторов контравариантного типа, но объяснение этого слишком длинное, поэтому я сокращусь), они могут толькобыть помещены в качестве выходных параметров в базовый тип, содержащий параметр типа.Таким образом, следующее определение является недопустимым из-за законов дисперсии:

final def getOrElse(default: => A): A

Поскольку A появляется здесь и в типе ввода, и в типе вывода.

0 голосов
/ 04 марта 2019

Луис хорошо объяснил, почему вы получили Any для вашего типа результата.Я просто хотел добавить, что если вы хотите использовать Double для дальнейшей обработки, но при этом правильно обрабатывать None, вы обычно делаете это с map, чтобы иметь возможность оставаться внутри Option доу вас действительно есть полезное значение для замены None, например:

val message: Option[String] = avg map {x => s"The average is $x."}
println(message getOrElse "Can't take average of empty sequence.")
0 голосов
/ 04 марта 2019

Как говорит подпись типа, getOrElse возвращает B, который является супертипом A (который является типом внутри Option ) .
Это имеет смысл, поскольку, если у вас есть Some (a: A) , то вы возвращаете a , который имеет тип A, что дает его подтипB, его можно без проблем повысить до B.И, если это Нет , то вы возвращаете значение по умолчанию , которое имеет тип B.

В вашем случае вы используете Нет для вашего значения по умолчанию, поэтому компилятор должен выяснить, какова наименее слабая верхняя граница между Double & Option[Nothing], чтоКак видите, это Any (потому что Any является супертипом каждого типа, и между иерархией Double & Option в иерархии типов нет другого отношения) .

Теперь, я полагаю, вы неправильно понимаете, как работает getOrElse, и какова цель None.
Я думаю, это то, что вы действительно хотите.

// Or any other double that makes sense as a default for you.
val avg: double = mean(xs).getOrElse(default = 0.0d)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...