为什么类型推断在这里失败了?
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
上进行Map也符合要求。
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)
3条答案
按热度按时间q3aa05251#
问:为什么
toSet
不能做我想做的事情?答:那太容易了。
问:但为什么这个不能编译?
List(1).toSet.map(x => ...)
答:Scala编译器无法推断
x
是Int
。问:什么,这是愚蠢的吗?
答:嗯,
List[A].toSet
不返回immutable.Set[A]
。对于某些未知的B >: A
,它返回immutable.Set[B]
。问:我怎么会知道呢?
答:来自Scaladoc。
问:但为什么
toSet
是这样定义的?答:你可能会认为
immutable.Set
是协变的,但它不是。它是不变的。并且toSet
的返回类型处于协变位置,因此不允许返回类型是不变的。问:你说的“协变立场”是什么意思?
答:让我帮你上维基百科:http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)。参见《奥德斯基,凡纳斯和勺子》第19章。
问:我现在明白了。但是为什么是一成不变的呢?设置不变量?
A:让我为您堆叠溢出:Why is Scala's immutable Set not covariant in its type?
问:我投降。我如何修复我的原始代码?
答:这行得通:
List(1).toSet[Int].map(x => ...)
。也是如此:List(1).toSet.map((x: Int) => ...)
(向Friedman&Felleisen道歉。THX向Paulp&Ijua寻求帮助)
**编辑:**在Adriaan's answer和在那里和这里的评论中的讨论中有宝贵的附加信息。
iqjalb3h2#
类型推断不能正常工作,因为
List#toSet
的签名编译器需要在调用中的两个位置推断类型。在函数中注解参数的另一种方法是使用显式类型参数调用
toSet
:更新:
关于您的问题,为什么编译器可以分两步推断它,让我们看看当您逐个键入行时会发生什么:
在这里,编译器将推断
ys
最具体的类型,在本例中是Set[Int]
。这种类型现在是已知的,因此可以推断传递给map
的函数的类型。如果您在示例中填写了所有可能的类型参数,则调用将编写为:
其中,第二个类型参数用于指定返回集合的类型(有关详细信息,请查看Scala集合是如何实现的)。这意味着我甚至低估了编译器必须推断的类型的数量。
在这种情况下,推断
Int
似乎很容易,但在某些情况下并非如此(考虑到Scala的其他特性,如隐式转换、单例类型、混合特性等)。我并不是说不能这样做--只是Scala编译器不能这样做。lx0bsm1f3#
我同意,即使在调用是连锁的情况下,推断“唯一可能的”类型也是很好的,但存在技术限制。
您可以将推理看作是对表达式的广度优先扫描,收集类型变量上的约束(从子类型边界和必需的隐式参数中产生),然后求解这些约束。该方法允许例如隐含以引导类型推理。在您的示例中,即使只有一个解决方案,如果您只查看
xs.toSet
子表达式,以后的链接调用可能会引入使系统无法满足的约束。不解决类型变量的缺点是闭包的类型推断要求目标类型是已知的,因此将失败(它需要一些具体的东西来继续--闭包的所需类型及其参数类型不能都是未知的)。现在,当延迟求解约束导致推理失败时,我们可以回溯,求解所有类型变量,然后重试,但这很难实现(而且可能非常低效)。