7

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?

Lachlan
  • 3,744
  • 2
  • 26
  • 29

2 Answers2

11

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.

Community
  • 1
  • 1
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
2

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)
thoredge
  • 12,237
  • 1
  • 40
  • 55