scala Either.RightProjection confusion (for comprehension de-sugaring)
either, for-loop, monads, scala
Solution
The fact that you cannot embed the `=` in the for-comprehension is related to this issue reported by Jason Zaugg; the solution is to Right-bias `Either` (or create a new data type isomorphic to it).
For your mega-confusion, you expanded the for sugar incorrectly. The desugaring of
for {
b <- x(a)
c <- y(b)
} yield z(c)
is
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
and not
x(a) flatMap { b => y(b)} map { c => z(c) }
Hence you should have done this:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
More fun about desugaring (the `j = i + 10` issue)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
is desugared into
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
So in your case, `y(b)` has result type `Either` which doesn't have `map` defined.
Problem
I can use an `=` in a scala for-comprehension (as specified in section 6.19 of the SLS) as follows: Option Suppose I have some function `String => Option[Int]`: ``` scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None } intOpt: (s: String)Option[Int] ``` Then I can use it thus ``` scala> for { | str <- Option("1") | i <- intOpt(str) | val j = i + 10 //Note use of = in generator | } | yield j res18: Option[Int] = Some(11) ``` It was my understanding that this was essentially equivalent to: ``` scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j } res19: Option[Int] = Some(11) ``` That is, the embedded generator was a way of injecting a `map` into a sequence of `flatMap` calls. So far so good. Either.RightProjection What I actually want to do: use a similar for-comprehension as the previous example using the `Either` monad. However, if we use it in a similar chain, but this time using the `Either.RightProjection` monad/functor, it doesn't work: ``` scala> def intEither(s: String): Either[Throwable, Int] = | try { Right(s.toInt) } catch { case x => Left(x) } intEither: (s: String)Either[Throwable,Int] ``` Then use: ``` 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 ^ ``` The issue has something to do with the function that a right-projection expects as an argument to its `flatMap` method (i.e. it expects an `R => Either[L, R]`). But modifying to not call `right` on the second generator, it still won't compile. ``` 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-Confusion But now I get doubly confused. The following works just fine: ``` 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) ``` But this does not: ``` 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 } ^ ``` I thought these were equivalent - What is going on? - How can I embed an `=` generator in a for comprehension across an `Either`?