13
votes

I typed the following into the Scala-REPL:

scala> List(1, 2, 3).toSet.subsets(2).map(_.toList)
res0: Iterator[List[Int]] = non-empty iterator

scala> List(1, 2, 3).toSet.subsets.map(_.toList)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toList)
              List(1, 2, 3).toSet.subsets.map(_.toList)

Why am I getting an error for the second line? Is this a bug in the compiler or am I missing something?

4
I would guess that the .subsets is more ambiguous (it could be a partial application of the version that takes an argument), so type inference works less well. In any case you can make it compile by doing what the compiler tells you and providing an explicit type: List(1, 2, 3).toSet.subsets.map{x: Set[Int] => x.toList}lmm
But the two methods have the same return type signature, according to 2.10.4 scaladoc: def subsets: Iterator[Set[A]], def subsets(len: Int): Iterator[Set[A]] So there should not be any difference, as far as types as concerned, right?the21st
Isn't it an inconsistency in Scala that these methods are allowed together while a method def subsets(i: Int)(d: Double): Iterator[Set[A]] would cause an ambiguous reference to overloaded definition error?Jasper-M
If it's Set(1, 2, 3).subsets it works. If you break it up with ...subsets match { case x => x.map(_.toList) } it also works.Rex Kerr
@the21st the asymmetry is that subsets could be a partial application of subsets(Int), whereas subsets(2) could not be a partial application of subsets. (But it's just a guess).lmm

4 Answers

5
votes

Paradoxically, the first version works because subsets in the application subsets(2) is, as it were, more ambiguous than without parens.

Because the method is overloaded, in the application, the compiler pauses to solve for the B result of toSet, and decides that B is Int. So it knows what param type is expected for the map.

In the version without parens, the method with a parameter list is not a candidate, because eta-expansion is not triggered. So when it types the map application, it has not drawn any conclusions about B, which is the input type to the mapping function.

The easy fix is to tell it to deduce B:

trait Test {
  def f1 = List(1, 2, 3).to[Set].subsets.map(_.toList) // instead of .toSet
  def f2 = List(1, 2, 3).toSet.subsets(2).map(_.toList)
}

The output of -Ytyper-debug on the original code shows how overload resolution gooses type inference:

|    |    |    |    |    |    \-> => Iterator[scala.collection.immutable.Set[B]] <and> (len: Int)Iterator[scala.collection.immutable.Set[B]]
|    |    |    |    |    solving for (B: ?B)
|    |    |    |    |    |-- 2 : pt=Int BYVALmode-EXPRmode-POLYmode (silent: method f2 in Test) 
|    |    |    |    |    |    \-> Int(2)
|    |    |    |    |    solving for (B: ?B)
|    |    |    |    |    \-> Iterator[scala.collection.immutable.Set[Int]]

Another workaround is to go through an extension method:

scala> implicit class ss[A](val s: Set[A]) { def ss(n: Int) = s subsets n ; def ss = s.subsets }
defined class ss

scala> List(1, 2, 3).toSet.ss.map(_.toList)
res1: Iterator[List[Int]] = non-empty iterator

Let's see if they'll take the library change:

https://github.com/scala/scala/pull/4270

4
votes

This is most likely to be a compiler issue itself that it fails to do type inference. I got a similar problem before.

If you take a quick peek of the definitions of both subsets() and subsets(len: Int) of Set, they share the same return type, so they should both work, right?

To solve the problem, this will work:

(List(1, 2, 3).toSet.subsets: Iterator[Set[Int]]).map(_.toList)
0
votes

Subsets can be partial application of the other overload.

0
votes

This appears to be a compiler issue.

Compare:

scala> List(1,2,3).toSet.subsets
res4: Iterator[scala.collection.immutable.Set[Int]] = non-empty iterator

scala> res4.map(_.toList)
res5: Iterator[List[Int]] = non-empty iterator