Search code examples
scalatypecheckingfor-comprehension

When is type checking done in for comprehension?


On the subject of desugaring for expressions with map, flatMap and withFilter, the book Programming In Scala 3rd Ed. says (in the chapter For Expression Revisited)

The translation of for expressions happens before type checking. This allows for maximum flexibility because the only requirement is that the result of expanding a for expression type checks.

However, in the REPL,

for (x: Int <- List("a", "b")) yield x

gives

<console>:12: error: scrutinee is incompatible with pattern type;  
 found   : Int  
 required: String  
       for(x: Int <- List("a", "b")) yield x  
              ^

and similarly,

for(x <- List("a", "b")) yield math.pow(x, 2)

gives

<console>:12: error: type mismatch;
 found   : String
 required: Double
       for(x <- List("a", "b")) yield math.pow(x, 2)
                                               ^

This seems to me to contradict what the book states, in that it appears type checking is happening before desugaring. Perhaps ...

  • I've misunderstood the book, in which case please enlighten me - in particular, what's type checked before and after desugaring?
  • The compiler type checks the resulting map, finds an inconsistency, then logs the problem in terms of the for expression for easier debugging

Solution

  • I believe your second suggestion is correct. Both of these type errors happen after translation, but it would be quite annoying if the compiler pointed you at some generated code when it gave you that error. Imagine it instead gave this message:

    <console>:12: error: type mismatch;
     found   : String
     required: Double
           List("a", "b").map(x => math.pow(x, 2))
                                            ^
    

    Then you have to sit there and ponder what this code is - you didn't write any calls to map or any anonymous function like that. And it would be 10x worse if the for-comprehension was more complicated, and resulted in multiple nested flatMaps and withFilters and stuff. So instead it points you to the pre-transformed version you actually wrote.