Frage

Warum diese Konstruktion Ursache eines Type Mismatch Fehler in 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)

Wenn ich den Teil mit der List-Schalter kompiliert es fein:

for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))

Das funktioniert auch gut:

for (first <- Some(1); second <- Some(2)) yield (first,second)
War es hilfreich?

Lösung

Für Comprehensions werden in Aufrufe an die map oder flatMap Verfahren umgewandelt. Zum Beispiel dieses:

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

wird, dass:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

Daher ist der erste Schleifenwert (in diesem Fall List(1)) den flatMap Methodenaufruf empfängt. Da flatMap auf einem List weiteren List zurückkehrt, wird das Ergebnis der für das Verständnis natürlich ein List sein. (Das war mir neu: Für Comprehensions nicht immer in Strömen führen, auch nicht unbedingt in Seqs.)

Nehmen Sie nun, einen Blick auf, wie flatMap in Option deklariert wird:

def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

Beachten Sie dies. Mal sehen, wie die fehlerhafte für das Verständnis (die mit Some(1)) auf eine Folge von Karte Anrufe umgewandelt wird:

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

Nun, es ist leicht zu sehen, dass der Parameter des flatMap Anrufs ist etwas, dass die Renditen eines List, aber keine Option, je nach Bedarf.

Um die Sache zu beheben, können Sie Folgendes tun:

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

Das compiles just fine. Es ist erwähnenswert, dass Option Bemerken ist kein Subtyp von Seq, wie oft angenommen wird.

Andere Tipps

Ein einfacher Tipp zu erinnern, für Comprehensions wird versuchen, die Art der Sammlung des ersten Generators zurückzukehren, Option [Int] in diesem Fall. Also, wenn Sie beginnen mit Manche (1) sollten Sie ein Ergebnis der Option [T] erwarten.

Wenn Sie ein Ergebnis wollen Liste Typ, sollten Sie mit einem Listengenerator starten.

Warum diese Einschränkung haben, und Sie nicht immer davon ausgehen, werden irgendeine Art von Sequenz wollen? Sie können eine Situation, wo es sinnvoll, Rückkehr Option macht. Vielleicht haben Sie eine Option[Int], dass Sie mit etwas verbinden wollen eine Option[List[Int]], sagen mit der folgenden Funktion zu erhalten: (i:Int) => if (i > 0) List.range(0, i) else None; Sie könnten dann diese schreiben und keine bekommen, wenn die Dinge nicht „sinnvoll“:

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

Wie für Comprehensions ist im allgemeinen Fall erweitert in der Tat ein ziemlich allgemeiner Mechanismus ein Objekt vom Typ M[T] mit einer Funktion (T) => M[U] zu kombinieren, um ein Objekt vom Typ M[U] zu bekommen. In Ihrem Beispiel kann M Option oder Liste sein. Im Allgemeinen hat es die gleiche Art M sein. So können Sie nicht Option mit Liste kombinieren. Beispiele für andere Dinge, die M, Blick auf sein können Subklassen dieses Merkmal .

Warum hat List[T] mit (T) => Option[T] Arbeit kombiniert, obwohl, wenn Sie mit der Liste gestartet? In diesem Fall nutzt die Bibliothek eine allgemeinere Art, wo es Sinn macht. So können Sie die Liste mit Travers kombinieren können, und es gibt eine implizite Konvertierung von Option zu Travers.

Unter dem Strich ist dies: darüber nachdenken, was geben Sie den Ausdruck einfügen möchten mit diesem Typ wie der erste Generator zurückzukehren und zu starten. Wickeln Sie es in dieser Art, falls erforderlich.

Es hat wahrscheinlich etwas mit der Option zu tun, nicht ein Iterable zu sein. Die implizite Option.option2Iterable wird den Fall behandeln, in denen Compiler zweite erwartet ein Iterable zu sein. Ich gehe davon aus, dass die Compiler Magie unterscheidet je nach Art der Schleifenvariable.

Ich fand immer dies hilfreich:

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)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top