Несоответствие типа на Scala для понимания
-
12-10-2019 - |
Вопрос
Почему эта конструкция вызывает ошибку несоответствия типа в Scala?
for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
<console>:6: error: type mismatch;
found : List[(Int, Int)]
required: Option[?]
for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
Если я переключу кое -что со списком, он хорошо составляет:
for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))
Это также работает нормально:
for (first <- Some(1); second <- Some(2)) yield (first,second)
Решение
Для понимания преобразуется в вызовы в map
или же flatMap
метод Например, это:
for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)
становится этим:
List(1).flatMap(x => List(1,2,3).map(y => (x,y)))
Следовательно, первое значение цикла (в данном случае, List(1)
) получит flatMap
метод вызов. С flatMap
на List
Возвращает другого List
, результат для понимания, конечно, будет List
. Анкет (Это было новым для меня: для понимания не всегда приводит к потокам, даже не обязательно в Seq
с.)
Теперь посмотрите, как flatMap
объявлен в Option
:
def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]
Имейте это в виду. Посмотрим, как ошибочно понимать Some(1)
) преобразуется в последовательность вызовов карты:
Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))
Теперь легко увидеть, что параметр flatMap
звонок - это то, что возвращает List
, но не Option
, как требуется.
Чтобы исправить эту вещь, вы можете сделать следующее:
for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)
Это хорошо компилирует. Стоит отметить, что Option
не подтип Seq
, как часто предполагается.
Другие советы
Легкий совет, чтобы помнить, для понимания Попробую вернуть тип коллекции первого генератора, опция [int] в этом случае. Итак, если вы начнете с Некоторые (1) Вы должны ожидать результата варианта [t].
Если вам нужен результат Список Введите, вы должны начать с генератора списков.
Почему это ограничение и не предполагать, что вы всегда захотите какую -то последовательность? У вас может быть ситуация, когда имеет смысл вернуться Option
. Анкет Может быть Option[Int]
что вы хотите объединить с чем -то, чтобы получить Option[List[Int]]
, скажем, со следующей функцией: (i:Int) => if (i > 0) List.range(0, i) else None
; Затем вы можете написать это и не получить, когда что -то не имеет смысла »:
val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns: Option[List[Int]] = None
Как для понимания расширяются в общем случае, на самом деле являются довольно общим механизмом объединения объекта типа M[T]
с функцией (T) => M[U]
Чтобы получить объект типа M[U]
. Анкет В вашем примере M может быть опцией или списком. В целом это должен быть тот же тип M
. Анкет Так что вы не можете объединить опцию с списком. Для примеров других вещей, которые могут быть M
, смотреть на подклассы этой черты.
Почему объединилось List[T]
с (T) => Option[T]
Работайте, хотя, когда вы начали со списка? В этом случае библиотека использует более общий тип, где это имеет смысл. Таким образом, вы можете объединить список с Traversable, и существует неявное преобразование из опции в Traverable.
Суть в следующем: подумайте о том, какой тип вы хотите, чтобы выражение вернулось, и начинать с этого типа в качестве первого генератора. Оберните его в этот тип, если это необходимо.
Вероятно, это как -то связано с тем, что вариант не является итерационным. Неявный Option.option2Iterable
Буду справиться с случаем, когда компилятор ожидает второго, чтобы стать итерабильным. Я ожидаю, что магия компилятора отличается в зависимости от типа переменной петли.
Я всегда находил это полезным:
scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))
scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
foo.flatten
^
scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))
scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)
scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)