Scala neanche la confusione di verniceproiezione (per la comprensione de-zucchero)
Domanda
Posso utilizzare un =
in una scala per comprensione (come specificato nella sezione 6.19 della SLS ) come segue:
Opzione
Supponiamo di avere qualche funzione String => Option[Int]
:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
.
Allora posso usarlo così
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
.
È stata la mia comprensione che questo è stato essenzialmente equivalente a:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
.
Cioè, il generatore incorporato è stato un modo di iniettare un map
in una sequenza di chiamate flatMap
. Finora così buono.
entrambi.RightProjection
Cosa voglio effettivamente fare : Utilizzare una simile comprensione per comprensione come esempio precedente utilizzando il Either
MONAD .
Tuttavia, se lo usiamo in una catena simile, ma questa volta usando il Either.RightProjection
Monad / Functor, non funziona:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
.
Quindi utilizzare:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
.
Il problema ha qualcosa a che fare con la funzione che una proiezione di destra si aspetta come argomento al suo metodo flatMap
(I.e. Si prevede un R => Either[L, R]
). Ma la modifica non chiamare right
sul secondo generatore, non si compilerà ancora.
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
.
Mega-confusione
Ma ora diventavo doppiamente confuso. I seguenti lavori stanno bene:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
.
Ma questo non è:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
.
Pensavo che questi fossero equivalenti
- .
- Cosa sta succedendo?
- Come posso incorporare un generatore di generatori di generatori in A per la comprensione attraverso un
=
?
Soluzione
Il fatto che non è possibile incorporare il =
nella For-comprensione è correlato a Questo problema riportato da Jason Zaugg;La soluzione è a destra-bias Either
(o creare un nuovo tipo di dati isomorfo ad esso).
Per la tua mega-confusione, hai ampliato lo zucchero in modo errato.Il Desugaring di
for {
b <- x(a)
c <- y(b)
} yield z(c)
.
è
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
.
e non
x(a) flatMap { b => y(b)} map { c => z(c) }
.
Quindi dovresti averlo fatto:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
.
Più divertimento su Desugaring (il problema del `J= I + 10`)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
.
è debbagliato in
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
.
Quindi nel tuo caso, y(b)
ha il tipo di risultato Either
che non ha map
definito.