Scala - an unexpected type of transition from Map to Iterable in to understand?

I am confused by the fact that typing takes place behind the scenes to understand maps. I understand that the external type of the collection should usually be preserved, and we see that the expected behavior in the following two cases:

scala> for { | (k,v) <- Map(0->1,2->3) | } yield k -> v res0: scala.collection.immutable.Map[Int,Int] = Map(0 -> 1, 2 -> 3) scala> for { | (k,v) <- Map(0->1,2->3) | foo = 1 | } yield k -> v res1: scala.collection.immutable.Map[Int,Int] = Map(0 -> 1, 2 -> 3) 

But when I add the second task inside the understanding, I get something amazing:

 scala> for { | (k,v) <- Map(0->1,2->3) | foo = 1 | bar = 2 | } yield k -> v res2: scala.collection.immutable.Iterable[(Int, Int)] = List((0,1), (2,3)) 

Why is this happening?

+5
source share
1 answer

If you run scala -Xprint:typer -e "for { ... } yield k->v" , you can get a disaggregated version of the code. Here is a very simplified version of what you get:

 val m: Map[Int,Int] = Map(0->1, 2->3) m.map { case x @ (k,v) => val foo = 1 val bar = 2 (x, foo, bar) }.map { case ((k,v), foo, bar) => (k, v) } 

So, you will notice that when for-comprehension is converted to a .map call, it actually returns foo and bar along with k->v , which means that it is Tuple3[(Int,Int), Int, Int] . Since the iterability of Tuple3 objects cannot be converted to Map , it is assumed that it should return Iterable . However, in order to get the correct output, which is a collection of Tuple2 objects, it executes a secondary .map that discards foo and bar from Tuple3 , but at the moment it no longer knows that it should have been Map , because when you call the calls on .map , this information is not carried forward.

Your one-assignment example is just lucky because the intermediate representation is Tuple2[(Int,Int), Int] .

On the other hand, if you use .map directly, it works:

 Map(0->1, 2->3).map { case (k,v) => val foo = 1 val bar = 2 k -> v } 
+5
source

All Articles