scala Bien.RightProjection confusión (para la comprensión de la adición de sacarosa)
Pregunta
Puedo usar un =
en una de scala para la comprensión (como se especifica en la sección 6.19 de la SLS) como sigue:
Opción
Supongamos que tengo alguna función String => Option[Int]
:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
A continuación, voy a usar así
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
Fue mi entendimiento de que esta era esencialmente equivalente a:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
Es decir, integrado en el generador era una manera de inyectar un map
en una secuencia de flatMap
las llamadas.Hasta ahora tan bueno.
Cualquiera de los dos.RightProjection
Lo que yo realmente quiero hacer: el uso de una similar para la comprensión del ejemplo anterior, el uso de la Either
mónada.
Sin embargo, si la utilizamos en una similar de la cadena, pero esta vez usando la Either.RightProjection
mónada/functor, no funciona:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
A continuación, utilice:
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
^
El problema tiene algo que ver con la función que el derecho de la proyección espera como un argumento a su flatMap
método (es decir,se espera que una R => Either[L, R]
).Pero la modificación no llamar right
en el segundo generador, todavía no se compilará.
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-Confusión
Pero ahora estoy doblemente confuso.El siguiente funciona bien:
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)
Pero esto no:
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 }
^
Pensé que estos eran equivalentes
- ¿Qué está pasando?
- ¿Cómo puedo insertar un
=
generador en un para la comprensión a través de unaEither
?
Solución
El hecho de que no se puede incrustar el =
en la comprensión está relacionada con este problema reportado por Jason Zaugg;la solución está a la Derecha del sesgo de Either
(o crear un nuevo tipo de datos isomorfo a ella).
Para su mega-confusión, se amplió el azúcar de forma incorrecta.El desugaring de
for {
b <- x(a)
c <- y(b)
} yield z(c)
es
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
y no
x(a) flatMap { b => y(b)} map { c => z(c) }
Por lo tanto, usted debería haber hecho esto:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
Más divertido acerca de la desugaring (la `j = i + 10` de la revista)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
es desugared en
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
Así que, en su caso, y(b)
tiene el tipo de resultado Either
el que no tiene map
definido.