Lachlan
Lachlan

Reputation: 3784

Why does this Scala for expression using tuples fail to compile?

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?

Upvotes: 7

Views: 2671

Answers (2)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297320

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.

Upvotes: 11

thoredge
thoredge

Reputation: 12661

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)

Upvotes: 2

Related Questions