为什么类型推断失败?
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)
另外,从另一方面来说,从Set
转换为List
,并在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)
答案 0 :(得分:21)
问:为什么toSet
没有做我想做的事?
List(1).toSet.map(x => ...)
答:Scala编译器无法推断x
是Int
。
答:好的,List[A].toSet
不会返回immutable.Set[A]
。对于某些未知的immutable.Set[B]
,它会返回B >: A
。
答:来自Scaladoc。
问:但为什么toSet
以这种方式定义?
答:您可能认为immutable.Set
是协变的,但事实并非如此。它是不变的。并且toSet
的返回类型处于协变位置,因此不能允许返回类型不变。
答:让我为你维基百科:http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)。另见Odersky,Venners&amp; Co.的第19章。匙。
问:我现在明白了。但为什么不可变。保持不变?答:请给我Stack Overflow:Why is Scala's immutable Set not covariant in its type?
问:我投降了。如何修复原始代码?答:这有效:List(1).toSet[Int].map(x => ...)
。这样做:List(1).toSet.map((x: Int) => ...)
(向Friedman&amp; Felleisen道歉。请向paulp&amp; ijuma求助)
编辑: Adriaan's answer以及评论中的讨论中都有宝贵的补充信息。
答案 1 :(得分:15)
由于List#toSet
的签名是
def toSet[B >: A] => scala.collection.immutable.Set[B]
并且编译器需要在调用中的两个位置推断出类型。在函数中注释参数的另一种方法是使用显式类型参数调用toSet
:
xs.toSet[Int] map (_*2)
更新:
关于你的问题为什么编译器可以分两步推断它,让我们看一下你逐个输入行时会发生什么:
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
的最具体类型,即Set[Int]
。此类型现在已知,因此可以推断传递给map
的函数的类型。
如果您在示例中填写了所有可能的类型参数,则调用将写为:
xs.toSet[Int].map[Int,Set[Int]](_*2)
其中第二个类型参数用于指定返回集合的类型(有关详细信息,请查看如何实现Scala集合)。这意味着我甚至低估了编译器必须推断出的类型数量。
在这种情况下,推断Int
似乎很容易,但有些情况并非如此(考虑到Scala的其他功能,如隐式转换,单例类型,特征作为mixins等)。我不是说它无法完成 - 只是Scala编译器没有这样做。
答案 2 :(得分:13)
我同意推断出“唯一可能”的类型会很好,即使是通过链接进行调用,但也存在技术限制。
您可以将推理视为对表达式的广度优先扫描,在类型变量上收集约束(由子类型边界和必需的隐式参数引起),然后解决这些约束。该方法允许例如暗示引导类型推断。在您的示例中,即使只有一个解决方案,如果您只查看xs.toSet
子表达式,以后链接的调用可能会引入使系统不可满足的约束。保持类型变量未解决的缺点是闭包的类型推断需要知道目标类型,因此会失败(它需要具体的东西 - 闭包所需的类型及其参数类型的类型必须不是两个都不知道)。
现在,当延迟解决约束使得推理失败时,我们可以回溯,解决所有类型变量,然后重试,但这很难实现(并且效率可能非常低)。