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 }