2011-09-02 7 views
14

Se creo una di comprensione con una definizione del valore con l'opzione, esso funziona come previsto:Perché Scala scegliere il tipo di 'prodotto' per 'per' le espressioni che coinvolgono entrambi i casi e le definizioni di valore

scala> for (a <- Some(4); b <- Some(5); val p = a * b) yield p 
res0: Option[Int] = Some(20) 

Fare la stessa cosa con entrambi i casi funziona se ho alcuna definizione del valore:

scala> for (a <- Right(4).right; b <- Right(5).right) yield a * b 
res1: Either[Nothing,Int] = Right(20) 

Ma se ho usato la definizione del valore, scala sembra di dedurre il tipo di contenitore sbagliato per la per la comprensione:

scala> for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
<console>:8: error: value map is not a member of Product with Serializable with Either[Nothing,(Int, Int)] 
for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
          ^

Perché lo fa? Quali sono i modi per aggirare questo comportamento?

risposta

19

I problemi provengono da val p = a*b Se si scrive il semplice

per (a < - destro (4) .right; b < - Destro (5) .right) cedere a * b

esso compila e ottieni il risultato corretto.

Il vostro problema ha due cause

In primo luogo, le proiezioni Eithermap e flatMap non hanno la firma al solito, vale a dire per le routine mappa e flatMap definiti in una classe generica M[A], (A => B) => M[B] e (A => M[B]) => M[B]. Il M[A] la routine sono definiti in è Either[A,B].RightProjection, ma in risultati e argomento, abbiamo Either[A,B] e non la proiezione.

In secondo luogo, il modo val p = a*b in per la comprensione è tradotto. Scala riferimento, 6,19 p 90:

Un generatore p < - e seguito da un valore definizione p '= e' è tradotto al seguente generatore di coppie di valori, dove x e x 'sono nomi freschi :

(p,p′) <- for([email protected]<-e) yield {val x′@p′ = e′; (x,x′)} 

Diamo semplificare il codice solo un po ', lasciando cadere il a <-. Inoltre, b e p rinominati in p e pp per essere più vicini alla regola di riscrittura, con pp per p'. a doveva essere portata per (p < - Destro (5) .right; val pp = a * p) resa pp

seguendo la regola, dobbiamo sostituire il generatore + definizione. Cosa c'è intorno a questo, for( e )yield pp, invariato.

for((p, pp) <- for([email protected] <- Right(5).right) yield{val [email protected] = a*p; (x,xx)}) yield pp 

L'interno per viene riscritto per una semplice mappa

for((p, pp) <- Right(5).right.map{case [email protected] => val [email protected] = a*p; (x,xx)}) yield pp 

qui è il problema. Il Right(5).right.map(...) è di tipo Either[Nothing, (Int,Int)], non Either.RightProjection[Nothing, (Int,Int)] come vorremmo. Non funziona in esterno per (che converte anche in map.Non esiste il metodo map su Either, è definito solo sulle proiezioni.

Se si guarda da vicino il vostro messaggio di errore, si dice così, anche se si parla Product e Serializable, si dice che si tratta di un Either[Nothing, (Int, Int)], e che nessuna mappa è definita su di esso. La coppia (Int, Int) proviene direttamente dalla regola di riscrittura.

Per la comprensione è destinato a funzionare bene nel rispetto della firma appropriata. Con il trucco con le proiezioni Either (che ha anche i suoi vantaggi), abbiamo questo problema.

+1

Ah, sta iniziando a dare un senso. Hai alcuni errori di battitura che mi hanno fatto inciampare all'inizio (numeri mancanti, errori di tipo sottile, ecc.) Che pulirò a breve. – srparish

+0

Il risultato interessante di quanto sopra è che se avrò tipi monadici coerenti starò bene. Quindi, se mi interessa solo proiezioni giuste, potrei creare un implicito come il seguente che risolve effettivamente il problema: implicit def RightProjection [A, B] (v: O [A, B]): Either.RightProjection [A, B] = v.right – srparish

+1

Ancora una volta, grazie per l'approfondita correzione! Avevo letto il riferimento e non avevo ancora capito cosa stava succedendo. Ovviamente in questo semplice esempio, un * b può essere spostato al rendimento, ma se questo è sostituito da un calcolo complesso che deve essere passato a una funzione che restituisce un altro E (ripeti questo più volte), essendo in grado di usare un intermedio l'assegnazione del valore può essere molto più pulita, quindi è necessario avere più livelli di dichiarazioni concatenate/rendimento. – srparish

Problemi correlati