使用.toSet创build的Settypes推断失败?

为什么types推断在这里失败?

scala> val xs = List(1, 2, 3, 3) xs: List[Int] = List(1, 2, 3, 3) scala> xs.toSet map(_*2) <console>:9: error: missing parameter type for expanded function ((x$1) => x$1.$times(2)) xs.toSet map(_*2) 

但是,如果分配了xs.toSet ,则会进行编译。

 scala> xs.toSet res42: scala.collection.immutable.Set[Int] = Set(1, 2, 3) scala> res42 map (_*2) res43: scala.collection.immutable.Set[Int] = Set(2, 4, 6) 

另外,换一种方式,转换为从List Set ,并在List映射遵守。

 scala> Set(5, 6, 7) res44: scala.collection.immutable.Set[Int] = Set(5, 6, 7) scala> res44.toList map(_*2) res45: List[Int] = List(10, 12, 14) 

我同意推断“唯一可能的”types,即使在通话链接时也是很好的,但是技术上有限制。

您可以将推论视为对expression式的广度优先扫描,收集typesvariables的约束(由子types边界和必需的隐式参数产生),然后解决这些约束。 这种方法允许,例如暗示引导types推断。 在你的例子中,即使只有一个解决scheme,如果你只看xs.toSet子expression式,以后的链接调用可能会引入约束,使系统不可满足。 留下未解决的typesvariables的缺点是对闭包的types推断需要知道目标types,因此会失败(它需要一些具体的继续 – 闭包的必需types和参数types的types必须不都是未知的)。

现在,当推迟解决约束条件导致推理失败时,我们可以回溯,解决所有的typesvariables,然后重试,但是实现起来很麻烦(可能效率很低)。

问:为什么不去做我想要的?

A:那太容易了。

问:但为什么不编译? List(1).toSet.map(x => ...)

答:Scala编译器无法推断x是一个Int

问:什么是愚蠢的?

A:那么List[A].toSet不会返回一个immutable.Set[A] 。 它返回一个immutable.Set[B]对于一些未知的B >: A

问:我怎么知道的?

答:从Scaladoc。

问:但为什么要定义这种方式?

答:你可能会认为是immutable.Set 。它是协变的,但事实并非如此。 这是不变的。 toSet的返回types处于协变位置,所以返回types不能是不变的。

问:你是什么意思,“协变的立场”?

答:让我为你维基百科: http : //en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) 。 另见Odersky,Venners&Spoon第19章。

问:我现在明白了。 但为什么是不可改变的。套不变?

答:让我为你堆栈溢出: 为什么斯卡拉的不可变集合在它的types不协变?

问:我投降了。 我如何解决我的原始代码?

答:这工作: List(1).toSet[Int].map(x => ...) 。 所以这个: List(1).toSet.map((x: Int) => ...)

(向Friedman&Felleisen道歉,thp to paulp&Ijuma求助)

编辑:在Adriaan的答案和在这里和这里的评论讨论中有宝贵的额外信息。

types推断不能正确工作,因为List#toSet的签名是

 def toSet[B >: A] => scala.collection.immutable.Set[B] 

编译器需要在你的调用中推断出两个地方的types。 在函数中注释参数的替代方法是使用显式types参数来调用toSet

 xs.toSet[Int] map (_*2) 

更新

关于你的问题,为什么编译器可以分两步推断它,让我们看看当你逐一input行时会发生什么:

 scala> val xs = List(1,2,3) xs: List[Int] = List(1, 2, 3) scala> val ys = xs.toSet ys: scala.collection.immutable.Set[Int] = Set(1, 2, 3) 

在这种情况下,编译器会推断出最为具体的ystypes,即Set[Int] 。 这种types现在是已知的,所以可以推断传递给map的函数的types。

如果您在示例中填写了所有可能的types参数,则调用将被写为:

 xs.toSet[Int].map[Int,Set[Int]](_*2) 

其中第二个types参数用于指定返回集合的types(有关详细信息,请查看Scala集合是如何实现的)。 这意味着我甚至低估了编译器必须推断的types数量。

在这种情况下,推断Int可能看起来很容易,但有些情况下(例如Scala的其他特性,如隐式转换,单例types,mixin特性等)。 我没有说它不能做 – 只是Scala编译器不这样做。