Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 697
Lachlan Avatar asked Apr 29 '11 11:04

Lachlan


2 Answers

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.

like image 91
Daniel C. Sobral Avatar answered Oct 27 '22 17:10

Daniel C. Sobral


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)
like image 24
thoredge Avatar answered Oct 27 '22 17:10

thoredge