Why does Scala choose the type 'Product' for 'for' expressions involving Either and value definitions
If I create a for comprehension with a value definition with Option, it works as expected:
scala> for (a <- Some(4); b <- Some(5); val p = a * b) yield p
res0: Option[Int] = Some(20)
Doing the same thing with Either works if i have no value definition:
scala> for (a <- Right(4).right; b 开发者_如何学Python<- Right(5).right) yield a * b
res1: Either[Nothing,Int] = Right(20)
But if i used the value definition, scala seems to infer the wrong container type for the for comprehension:
scala> for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p
<console>:8: error: value map is not a member of Product with Serializable with Either[Nothing,(Int, Int)]
for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p
^
Why does it do this? What ways around this behavior are available?
The problems comes from val p = a*b
If you write the simpler
for (a <- Right(4).right; b <- Right(5).right) yield a*b
it compiles and you get the proper result.
Your problem has two causes
First, the Either
projections map
and flatMap
do not have the usual signature , namely for routines map and flatMap defined in a generic class M[A]
, (A => B) => M[B]
and (A => M[B]) => M[B]
. The M[A]
the routine are defined in is Either[A,B].RightProjection
, but in results and argument, we have Either[A,B]
and not the projection.
Second, the way val p = a*b
in the for comprehension is translated. Scala Reference, 6.19 p 90:
A generator p <- e followed by a value definition p′ = e′ is translated to the following generator of pairs of values, where x and x′ are fresh names:
(p,p′) <- for(x@p<-e) yield {val x′@p′ = e′; (x,x′)}
Let's simplify the code just a little bit, dropping the a <-
. Also, b
and p
renamed to p
and pp
to be closer to the rewrite rule, with pp
for p'
. a
supposed to be in scope
for(p <- Right(5).right; val pp = a*p) yield pp
following the rule, we have to replace the generator + definition. What is around that, for(
and )yield pp
, unchanged.
for((p, pp) <- for(x@p <- Right(5).right) yield{val xx@pp = a*p; (x,xx)}) yield pp
The inner for is rewritten to a simple map
for((p, pp) <- Right(5).right.map{case x@p => val xx@pp = a*p; (x,xx)}) yield pp
Here is the problem. The Right(5).right.map(...)
is of type Either[Nothing, (Int,Int)]
, not Either.RightProjection[Nothing, (Int,Int)]
as we would want. It does not work in the outer for (which converts to a map
too. There is no map
method on Either
, it is defined on projections only.
If you look closely at your error message, it says so, even if it mentions Product
and Serializable
, it says that it is an Either[Nothing, (Int, Int)]
, and that no map is defined on it. The pair (Int, Int)
comes directly from the rewrite rule.
The for comprehension is intended to work well when respecting the proper signature. With the trick with Either
projections (which has its advantages too), we get this problem.
精彩评论