使用.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)
在这种情况下,编译器会推断出最为具体的ys
types,即Set[Int]
。 这种types现在是已知的,所以可以推断传递给map
的函数的types。
如果您在示例中填写了所有可能的types参数,则调用将被写为:
xs.toSet[Int].map[Int,Set[Int]](_*2)
其中第二个types参数用于指定返回集合的types(有关详细信息,请查看Scala集合是如何实现的)。 这意味着我甚至低估了编译器必须推断的types数量。
在这种情况下,推断Int
可能看起来很容易,但有些情况下(例如Scala的其他特性,如隐式转换,单例types,mixin特性等)。 我没有说它不能做 – 只是Scala编译器不这样做。