当前位置:  开发笔记 > 编程语言 > 正文

为什么Scala的不可变Set在其类型中不协变?

如何解决《为什么Scala的不可变Set在其类型中不协变?》经验,为你挑选了3个好方法。

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

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
}

Daniel Spiew.. 53

Set由于集合作为函数的概念,它的类型参数是不变的.以下签名应略微澄清:

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

如果Set是协变的A,由于函数的逆变,该apply方法将不能采用类型的参数A. Set可能是逆变A,但是当你想做这样的事情时,这也会导致问题:

def elements: Iterable[A]

简而言之,最好的解决方案是使事物保持不变,即使对于不可变数据结构也是如此.你会注意到它的immutable.Map一个类型参数也是不变的.



1> Daniel Spiew..:

Set由于集合作为函数的概念,它的类型参数是不变的.以下签名应略微澄清:

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

如果Set是协变的A,由于函数的逆变,该apply方法将不能采用类型的参数A. Set可能是逆变A,但是当你想做这样的事情时,这也会导致问题:

def elements: Iterable[A]

简而言之,最好的解决方案是使事物保持不变,即使对于不可变数据结构也是如此.你会注意到它的immutable.Map一个类型参数也是不变的.


类型签名是一个相当薄弱的例子.集合的"apply"与它包含方法的内容相同.唉,Scala的List是共变体,并且也有一个contains方法.当然,List的包含签名是不同的,但该方法与Set的方法类似.因此,除了设计决策之外,没有什么能真正阻止Set变为共变体.
我仍然不相信牺牲协方差对于Set来说是值得的.当然,它很好,它是一个谓词,但你通常只是更冗长一点,并使用"set.contains"而不是"set"(并且可以说在许多情况下"set.contains"读取更好).
从数学角度来看,集合不是布尔函数.集合是由Zermelo-Fraenkel公理"建立起来的"而不是通过某种包含函数*减少*.这背后的原因是拉塞尔的悖论:如果任何东西都可以成为一个集合的成员,那么考虑不属于他们自己的集合的集合R. 然后问问题,R是R的成员吗?
我想这个论点取决于"作为函数设置背后的概念" - 这可以扩展吗?例如,"作为一个功能集"的优点是什么,"设置为集合"不是吗?是否值得失去使用这种协变类型?
@Martin:因为List的contains方法采用Any而不是A.`List(1,2,3).contains _`的类型是`(Any)=> Boolean`,而`Set的类型(1,2, 3).contains _`是`res1:(Int)=> Boolean`.
去"包含"并没有解决问题.该方法的参数类型需要相反地变化,与"Set"是否扩展"A => Boolean"没有什么不同.事实上,这些案例很容易同构,所以仅仅消除"apply"是不够的.
为什么List可以协变,然后它还有一个apply方法?

2> Seth Tisue..:

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

"关于集合的问题,我认为非变量也源于实现.常见集合实现为哈希表,它是密钥类型的非变量数组.我同意这是一个有点烦人的不规则性."

因此,似乎我们为此构建原则性理由的所有努力都是误导的:-)


这可以通过在内部存储`Array [Any]`来解决.

3> Jorge Ortiz..:

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

Scala的类型推断足以让你知道在某些情况下你需要CharSequences而不是字符串.特别是,以下内容适用于2.7.3:

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

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

推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有