为什么Scala的不可变集的类型不协变?


94

编辑:根据原始答案改写了这个问题

scala.collection.immutable.Set班是不是在它的类型参数不变性。为什么是这样?

import scala.collection.immutable._

def foo(s: Set[CharSequence]): Unit = {
    println(s)
}

def bar(): Unit = {
   val s: Set[String] = Set("Hello", "World");
   foo(s); //DOES NOT COMPILE, regardless of whether type is declared 
           //explicitly in the val s declaration
}

值得一提的是foo(s.toSet[CharSequence])编译还不错。该toSet方法为O(1)-仅包装asInstanceOf
约翰·沙利文2013年

1
还要注意,它也可以foo(Set("Hello", "World"))在2.10 上编译,因为Scala似乎能够推断出Set的正确类型。但是,它不适用于隐式转换(stackoverflow.com/questions/23274033/…)。
LP_ 2014年

Answers:


55

Set由于设置为函数的概念,其类型参数是不变的。以下签名应使情况有所澄清:

trait Set[A] extends (A=>Boolean) {
  def apply(e: A): Boolean
}

如果Set在中是协变的A,则由于功能的差异,该apply方法将无法采用类型的参数ASet可能是逆变A,但是当你想要做这样的事情这也导致问题:

def elements: Iterable[A]

简而言之,最好的解决方案是即使对于不变的数据结构,也要保持不变。您会注意到,immutable.Map它的类型参数之一也是不变的。


4
我猜想这个论点围绕着“集合作为函数的背后的概念”-是否可以扩展?例如,“设置为函数”给我的“设置为集合”没有什么优势?失去使用该协变类型值得吗?
oxbow_lakes 2009年

23
类型签名是一个比较弱的示例。集合的“应用”与包含方法相同。Sc,Scala的List是协变的,并且也包含一个contains方法。当然,List的包含的签名是不同的,但是该方法的工作方式与Set的相同。因此,除了设计决策外,没有什么真正阻止Set成为协变的。
Daniel C. Sobral

6
从数学的角度来看,集合不是布尔函数。集合是根据Zermelo-Fraenkel公理“构建”的,没有因某些包含函数而减少。这背后的原因是罗素的悖论:如果任何事物都可以成为集合的成员,则考虑不是集合本身的集合的集合R。然后问一个问题,R是R的成员吗?
oxbow_lakes

12
我仍然不相信牺牲协方差对于Set是值得的。当然,这是一个谓词很好,但是您通常可以稍微冗长一些,并使用“ set.contains”而不是“ set”(而且在很多情况下,“ set.contains”的阅读效果更好)。
Matt R

4
@马丁:因为名单的contains方法采取任何不答类型的List(1,2,3).contains _(Any) => Boolean,虽然类型Set(1,2,3).contains _res1: (Int) => Boolean
塞斯·提苏

52

http://www.scala-lang.org/node/9764上, Martin Odersky写道:

“在集合的问题上,我认为不变性也源于实现。通用集合被实现为哈希表,它们是键类型的不变数组。我同意这是一个令人讨厌的不规则性。”

因此,似乎我们为建立这一原则的所有努力都被误导了:-)


1
但是一些序列也用数组实现,但仍然Seq是协变的……我错过了什么吗?
LP_ 2014年

4
这可以通过Array[Any]内部存储来简单地解决。
2015年

@rightfold是正确的。可能有合理的原因,但事实并非如此。
保罗·德雷珀

6

编辑:对于任何想知道为什么这个答案似乎有点偏离主题的人,这是因为我(提问者)已经修改了问题。

Scala的类型推断足以确定您在某些情况下需要CharSequences而不是String。特别是,在2.7.3中,以下对我有用:

import scala.collections.immutable._
def findCharSequences(): Set[CharSequence] = Set("Hello", "World")

至于如何直接创建immutable.HashSets:不用。作为实现的优化,少于5个元素的immutable.HashSet实际上不是immutable.HashSet的实例。它们是EmptySet,Set1,Set2,Set3或Set4。这些类是immutable.Set的子类,但不是immutable.HashSet的子类。


你是对的; 在尝试简化我的实际示例时,我犯了一个小错误:-(
oxbow_lakes 2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.