Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

missing parameter type error by calling toSet

Tags:

scala

Why this code doesn't work:

scala> List('a', 'b', 'c').toSet.subsets.foreach(e => println(e))

<console>:8: error: missing parameter type
              List('a', 'b', 'c').toSet.subsets.foreach(e => println(e))
                                                        ^

But when I split it then it works fine:

scala> val itr=List('a', 'b', 'c').toSet.subsets
itr: Iterator[scala.collection.immutable.Set[Char]] = non-empty iterator

scala> itr.foreach(e => println(e))
Set()
Set(a)
Set(b)
Set(c)
Set(a, b)
Set(a, c)
Set(b, c)
Set(a, b, c)

And this code is OK as well:

Set('a', 'b', 'c').subsets.foreach(e => println(e))
like image 609
Oleg Pavliv Avatar asked Oct 27 '12 05:10

Oleg Pavliv


1 Answers

First, there's a simpler version of the code that has the same issue:

List('a', 'b', 'c').toSet.foreach(e => println(e))

This doesn't work either

List('a', 'b', 'c').toBuffer.foreach(e => println(e))

However, these work just fine:

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 go take a look at the List class documentation you'll see that the methods that work return some type parameterized with A, whereas methods that don't work return types parameterized with B >: A. The problem is that the Scala compiler can't figure out which B to use! That means it will work if you tell it the type:

List('a', 'b', 'c').toSet[Char].foreach(e => println(e))

Now as for why toSet and toBuffer have that signature, I have no idea...

Lastly, not sure if this is helpful, but this works too:

// I think this works because println can take type Any
List('a', 'b', 'c').toSet.foreach(println)

Update: After poking around the docs a little bit more I noticed that the method works on all the types with a covariant type parameter, but the ones with an invariant type parameter have the B >: A in the return type. Interestingly, although Array is invariant in Scala they provide two version of the method (one with A and one with B >: A), which is why it doesn't have that error.

I also never really answered why breaking the expression into two lines works. When you simply call toSet on its own, the compiler will automatically infer A as B in the type for the resulting Set[B], unless you do give it a specific type to pick. This is just how the type inference algorithm works. However, when you throw another unknown type into the mix (i.e. the type of e in your lambda) then the inference algorithm chokes and dies—it just can't handle an unknown B >: A and an unknown type of e as well.

like image 120
DaoWen Avatar answered Oct 22 '22 00:10

DaoWen