2015-09-18 12 views
6
for { 
    a <- Some(1) 
    b <- Some(2) 
} yield (a, b) 

rendimenti Some((1, 2))Scala per-la comprensione con decomposizione tuple

for { 
    a <- Right(1).right 
    b <- Left(2).left 
} yield (a, b) 

restituisce Left((1, 2))


Ora voglio decomporsi tuple in per la comprensione.

for { 
    (a, b) <- Some((1, 2)) 
    (c, d) <- Some((3, 4)) 
} yield (a, b, c, d) 

rendimenti Some((1, 2, 3, 4))

for { 
    (a, b) <- Right((1, 2)).right 
    (c, d) <- Left((3, 4)).left 
} yield (a, b, c, d) 

fallisce la compilazione:

error: constructor cannot be instantiated to expected type; 
found : (T1, T2) 
required: scala.util.Either[Nothing,(Int, Int)] 
        (a, b) <- Right((1, 2)).right 

error: constructor cannot be instantiated to expected type; 
found : (T1, T2) 
required: scala.util.Either[(Int, Int),Nothing] 

Perché non quest'ultimo esempio funziona? Qual è la differenza?

+1

C'è un [Bug] (https://issues.scala-lang.org/browse/SI-5589) hanno riportato per questo problema. – emilianogc

risposta

3

Questo è un bug:

SI-5589: For-comprehension on Either.RightProjection with Tuple2 extractor in generator fails to compile

withFilter() è chiamato (certa documentazione fa riferimento a filter(), ma che è stato cambiato in 2.8), che mette a rischio l'inferenza del tipo.

withFilter() viene utilizzato per cose come for(a <- b if c), sebbene in base allo 6.19 non dovrebbe essere utilizzato in questo caso.

Quest'ultimo bug viene catturato in SI-1336: spec requires type checking of for-comprehension to consider refutability, che è stato aperto per sette anni (2008).

Forse una futura generazione troverà la soluzione.


Vedi why does filter have to be defined for pattern matching in a for loop in scala?

3

Questa potrebbe essere una limitazione per le espressioni. Traducendo

for { 
    (a, b) <- Some((1, 2)) 
    (c, d) <- Some((3, 4)) 
} yield (a, b, c, d) 

in

Some((1, 2)).flatMap({case(a, b) => 
    Some((3, 4)).map({case (c, d) => 
    (a, b, c, d) 
    }) 
}) 

funziona in due modi. Con l'espressione Either, funziona solo la versione mappa/flatMap .

for { 
    (a, b) <- Right((1, 2)).right 
    (c, d) <- Left((3, 4)).left 
} yield (a, b, c, d) 


Right((1, 2)).right.flatMap({ 
    case(a, b) => Left((3, 4)).left.map({case (c, d) => 
    (a, b, c, d) 
    }) 
}) 

Non consiglio usando Either, invece utilizzare il tipo di \/ da scalaz. http://eed3si9n.com/learning-scalaz/Either.htmlEither non è sinistro o destro-pendente, che è un problema perché non specifica dove l'errore o il valore va.

+1

'Either' potrebbe non essere a destra oa sinistra, ma' LeftProjection' e 'RightProjection' sono certamente! –

+1

@PaulDraper yes, ma poi dovrai specificare '.right' o' .left' ogni volta che vuoi usarlo. E preferisco il mio codice senza rumore. – Reactormonk

4

Poiché i generatori per (qualunque, in qualunque) < - O non sono filtri "inconfutabili" sono aggiunte nel codice Dezuccherato (why does filter have to be defined for pattern matching in a for loop in scala?), con conseguente:

Right((1, 2)).right.filter { case (a, b) => true; case _ => false }.flatMap({ 
    case(a, b) => Left((3, 4)).left.filter { case (c, d) => true; case _ => false }.map({case (c, d) => 
    (a, b, c, d) 
    }) 
}) 

I filtri sono in cui si verifica l'errore di compilazione perché il metodo del filtro per la Destra si presenta così (quella di sinistra è simile):

def filter[X](p: B => Boolean): Option[Either[X, B]] = e match { 
    case Left(_) => None 
    case Right(b) => if(p(b)) Some(Right(b)) else None 
} 

il che significa che il compilatore sta cercando di effettuare le seguenti operazioni:

Che fallisce dal momento che (T1, T2) non può essere inserito in O [A, B] (cosa si estende a destra) dove A è Nothing e B è (Int, Int).

È possibile ottenere qualcosa di simile a questo utilizzando:

for { 
    a <- Right((1, 2)).right 
    b <- Left((3, 4)).left 
} yield (a, b) match { 
    case ((c, d), (e, f)) => (c, d, e, f) 
    case _ => 
}