64

Почему эта конструкция вызывает ошибку несоответствия типа в Scala?Несоответствие типа 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) 
+2

В каком результате вы ожидали, что Scala вернется в неудачном примере? –

+0

Когда я писал это, я думал, что я получу опцию [List [(Int, Int)]]. –

ответ

99

Для постижений преобразуются в вызовы метода 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] 

Keep это в виду. Давайте посмотрим, как ошибочный для понимания (тот, с 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, как это часто принято считать.

4

Возможно, это связано с тем, что Вариант не является Iterable. Неявный Option.option2Iterable будет обрабатывать случай, когда компилятор ожидает, что второй будет Iterable. Я ожидаю, что магия компилятора отличается в зависимости от типа переменной цикла.

24

Простой наконечник для запоминания, для понимания попытается вернуть тип коллекции первого генератора Option [Int] в этом случае. Итак, если вы начинаете с Некоторые (1) вы должны ожидать результата опциона [T].

Если вы хотите получить результат Список, вам следует начать с генератора List.

Зачем это ограничение и не предполагать, что вы всегда захотите какой-то последовательности? У вас может быть ситуация, когда имеет смысл вернуть Option.Возможно, у вас есть Option[Int], который вы хотите совместить с чем-то, чтобы получить Option[List[Int]], скажем, со следующей функцией: (i:Int) => if (i > 0) List.range(0, i) else None; вы могли бы написать это и получить 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 может быть Option или List. В общем, он должен быть того же типа M. Таким образом, вы не можете комбинировать опцию со списком. Для примеров других вещей, которые могут быть M, посмотрите на subclasses of this trait.

Почему сочетание List[T] с (T) => Option[T] работает, хотя, когда вы начали со Списка? В этом случае библиотека использует более общий тип, где это имеет смысл. Таким образом, вы можете комбинировать List with Traversable, и есть неявное преобразование из Option в Traversable.

Суть в том, что: подумайте о том, какой тип вы хотите, чтобы выражение возвращалось и начиналось с этого типа в качестве первого генератора. При необходимости оберните его в этом типе.

Смежные вопросы