Domanda

Perché questa costruzione causa un errore di tipo non corrispondente a 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)

Se posso passare il Alcuni con la Lista compila bene:

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

Questo funziona anche bene:

for (first <- Some(1); second <- Some(2)) yield (first,second)
È stato utile?

Soluzione

Per comprensioni sono convertiti in chiamate al metodo map o flatMap. Per esempio questa:

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

diventa che:

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

Pertanto, il primo valore loop (in questo caso, List(1)) riceverà la chiamata metodo flatMap. Dal momento che flatMap su un List ritorna un'altra List, il risultato della per la comprensione sarà ovviamente un List. (Questo è stato nuovo per me: per comprensioni non sempre si traducono in corsi d'acqua, neanche necessariamente in Seqs.)

Ora, date un'occhiata a come flatMap è dichiarata in Option:

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

Tenere questo in mente. Vediamo come l'erronea per la comprensione (quello con Some(1)) viene convertito in una sequenza di chiamate mappa:

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

Ora, è facile vedere che il parametro della chiamata flatMap è qualcosa che restituisce un List, ma non un Option, come richiesto.

Al fine di risolvere la cosa, è possibile effettuare le seguenti operazioni:

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

che compila bene. Vale la pena notare che Option non è un sottotipo di Seq, come spesso si suppone.

Altri suggerimenti

Una punta facile da ricordare, espressioni for cercherà di restituire il tipo della collezione del primo generatore, Riservato [Int] in questo caso. Quindi, se si inizia con Alcuni (1) si dovrebbe aspettare un risultato di Opzione [T].

Se si desidera un risultato di List tipo, si dovrebbe iniziare con un generatore di lista.

Perché questa restrizione e non assume sarai sempre desidera una sorta di sequenza? Si può avere una situazione in cui ha senso Option ritorno. Forse avete un Option[Int] che si desidera combinare con qualcosa per ottenere un Option[List[Int]], dice con la seguente funzione: (i:Int) => if (i > 0) List.range(0, i) else None; si potrebbe quindi scrivere questo e ottenere Nessuno quando le cose non "ha senso":

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

Come per comprehensions sono espansi nel caso generale sono infatti un meccanismo abbastanza generale di combinare un oggetto di tipo M[T] con funzione (T) => M[U] per ottenere un oggetto di tipo M[U]. Nel tuo esempio, M può essere Opzione o List. In generale deve essere dello stesso tipo M. Quindi non è possibile combinare con opzione List. Per esempi di altre cose che possono essere M, un'occhiata a sottoclassi di questo tratto .

Perché unisce List[T] con il lavoro (T) => Option[T] anche se quando si è iniziato con la lista? In questo caso la libreria di utilizzare un tipo più generale in cui ha senso. Così si può combinare con Lista Traversable e c'è una conversione implicita da Opzione per Traversable.

La linea di fondo è questo: pensare a che tipo si desidera che l'espressione di tornare e iniziare con quel tipo, come il primo generatore. Avvolgerlo in quel tipo, se necessario.

Probabilmente ha qualcosa a che fare con l'opzione non essendo un Iterable. L'implicita Option.option2Iterable gestirà il caso in cui il compilatore si aspetta secondo per essere un Iterable. Mi aspetto che la magia compilatore è diverso a seconda del tipo di variabile del ciclo.

Ho sempre trovato questa utile:

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top