confusion scala Soit.RightProjection (pour la compréhension du désucrage)
Question
Je peux utiliser un =
dans une échelle de compréhension (comme spécifié dans la section 6.19 du SLS) comme suit:
Option
Supposons que j'ai une fonction String => Option[Int]
:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
Alors je peux l'utiliser ainsi
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
J'avais cru comprendre que cela équivalait essentiellement à :
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
Autrement dit, le générateur intégré était un moyen d'injecter un map
dans une séquence de flatMap
appels.Jusqu'ici, tout va bien.
Soit.RightProjection
Ce que je veux réellement faire: utilisez une méthode de compréhension similaire à celle de l'exemple précédent en utilisant le Either
monade.
Cependant, si nous l'utilisons dans une chaîne similaire, mais cette fois en utilisant le Either.RightProjection
monade/foncteur, ça ne marche pas :
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
Utilisez ensuite :
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
^
Le problème a quelque chose à voir avec la fonction qu'une projection à droite attend comme argument de son flatMap
méthode (c'est-à-direil attend un R => Either[L, R]
).Mais je modifie pour ne pas appeler right
sur le deuxième générateur, il ne sera toujours pas compilé.
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)
^
Méga-Confusion
Mais maintenant, je suis doublement confus.Ce qui suit fonctionne très 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)
Mais ce n’est pas le cas :
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 }
^
je pensais que c'était équivalent
- Que se passe-t-il?
- Comment puis-je intégrer un
=
générateur dans un pour la compréhension à travers unEither
?
La solution
Le fait que vous ne puissiez pas intégrer le =
dans le pour-compréhension est lié à ce problème rapporté par Jason Zaugg;la solution est de biaiser à droite Either
(ou créez un nouveau type de données isomorphe à celui-ci).
Pour votre méga-confusion, vous avez mal développé le sucre.Le désucrage de
for {
b <- x(a)
c <- y(b)
} yield z(c)
est
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
et pas
x(a) flatMap { b => y(b)} map { c => z(c) }
Tu aurais donc dû faire ceci :
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
Plus de plaisir à propos du désucrage (le problème `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)
est désucré dans
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
Donc dans ton cas, y(b)
a un type de résultat Either
qui n'a pas map
défini.