With Scala 2.8.1, compiling this:
val t = (40, 2)
println(for ((i, j) <- List(t)) yield i + j)
val e: Either[String, (Int, Int)] = Right(t)
println(e.right.map {
case (i, j) => i + j
})
println(for ((i, j) <- e.right) yield i + j)
gives this:
test.scala:9: error: constructor cannot be instantiated to expected type;
found : (T1, T2)
required: Either[Nothing,(Int, Int)]
println(for ((i, j) <- e.right) yield i + j)
According to Programming in Scala, the for expression should be equivalent to the map/case expression, but only the latter compiles. What am I doing wrong, and how should I do this?
Actually, that is not quite the translation that is happening. You may refer to this answer for a more complete guide, but this case is not explicitly mentioned even there.
What happens is that a for comprehension with pattern matching filters the non-matching case. For example,
for((i, j) <- List((1, 2), 3)) yield (i, j)
will return List((1, 2)): List[(Any, Any)]
, as withFilter
is called first. Now, Either
doesn't seem to have withFilter
, so it will use filter
, and here's the actual translation of that for comprehension:
e.right.filter { case (i, j) => true; case _ => false }.map { case (i, j) => i + j }
Which gives exactly the same error. The problem is that e.right
returns a RightProjection
, but filter
on RightProjection[A, B]
returns Option[Either[Nothing, B]]
.
The reason for that is that there is no such thing as an "empty" Either
(or RightProjection
), so it needs to encapsulate its result on an Option
.
Having said all that, it is truly surprising when looked at the for-comprehension level. I think the right thing would be for filter
to return some kind of filtered projection instead.
Right does not return the option you might expect, but a RightProjection. This fixes it:
println(for ((i, j) <- e.right.toOption) yield i + j)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With