Firstly, there is a simpler version of the code that has the same problem:
List('a', 'b', 'c').toSet.foreach(e => println(e))
This does not work.
List('a', 'b', 'c').toBuffer.foreach(e => println(e))
However, these works are simply wonderful:
List('a', 'b', 'c').toList.foreach(e => println(e)) List('a', 'b', 'c').toSeq.foreach(e => println(e)) List('a', 'b', 'c').toArray.foreach(e => println(e))
If you look at the List documentation for the class , you will see that the methods that work return a type parameterized with A , while the methods that don't work return the types parameterized with B >: A The problem is that the Scala compiler cannot determine which B use! This means that it will work if you specify the type:
List('a', 'b', 'c').toSet[Char].foreach(e => println(e))
Now, why toSet and toBuffer have this signature, I have no idea ...
Finally, not sure if this is useful, but this also works:
// I think this works because println can take type Any List('a', 'b', 'c').toSet.foreach(println)
Update: After I looked into the documents several times, I noticed that this method works in all types with a covariance type parameter, but those that have an invariant type parameter have B >: A in the returned type. Interestingly, although Array is invariant in Scala, they provide two versions of the method (one with A and one with B >: A ), so it does not have this error.
I also never answered why splitting an expression into two lines works. When you simply call toSet yourself, the compiler automatically outputs A as B to the type for the resulting Set[B] , unless you let it choose a specific type. This is how the type inference algorithm works. However, when you inject another unknown type into the mix (i.e., Type e in your lambda), the output algorithm throttles and dies - it simply cannot process the unknown B >: A and unknown type e as well.