为什么GenSet声明具有不变的类型参数?

时间:2013-06-27 20:53:51

标签: scala scala-collections

我有一个类型Foo,带有子类型。

我有另一个类,其中包含一个Foo:

class FooResult(val foo: Foo ...

我有一套FooResult,我希望用map()拉出Foos,然后在该Set(Foos)和另一组Foos之间计算差异。下面的'结果'是一个Set [FooResult]并且 - 这是关键部分 - bundle是一个Set [_<:Foo]

val completedFoos = results.map(result => result.calc)
val unfinishedFoos = bundle.foos.diff(completedCalcs)

第二行不会编译。当bundle是Set [Foo]而不是Set [_<:Foo]时它工作正常 - 引入协方差搞砸了。这是错误:

type mismatch;
found   : Set[Foo]
required: scala.collection.GenSet[_$1]
Note: Foo >: _$1, but trait GenSet is invariant in type A.
You may wish to investigate a wildcard type such as `_ >: _$1`. (SLS 3.2.10)

我无法找到任何简单的方法。请原谅我的无知,但为什么像GenSet这样的'帮助'类型会被声明为不变的?

我是否遗漏了某些东西(非常可能)或者这是否是Scala奇妙收藏框架的一个弱点(我认为这不太可能)?

1 个答案:

答案 0 :(得分:3)

这是一个有意识的设计决策,使Set.contains / apply typesafe。你不应该有一个s:设置[Int]并意外地做一些类似s.contains(“x”)的东西,它总是假的,所以可能不是你想要的。此外,Set [T]实现了Function [T,Boolean],这只有在apply方法不采用Any时才有效。

在scala-user邮件列表上有关于此主题的无休止的讨论。例如,请参阅thisthis

以下是Paul Phillips在第二次讨论中的引用,其中总结了基本原理:

“是的,apply(别名为contains)是Set的中心操作,而 在Seq。

中确实包含“只是一些方法”

没有人说你有时候不想要协变套装,但总的来说, 它是更有用的不变量。“

请注意,您始终可以使用隐式转换向集合添加协方差。这意味着如果您有例如设置[Int]和采用Set [Any]的方法,它将起作用。但这也意味着你现在可以不小心调用Set [Int] .contains(“x”),并且编译器不会捕获错误(你总是会得到错误)。

scala> implicit def setIsCovariant[T,U <: T](s:Set[U]):Set[T] = s.asInstanceOf[Set[T]]
setIsCovariant: [T, U <: T](s: Set[U])Set[T]

scala> val s : Set[Int] = Set(1,2,3,4)
s: Set[Int] = Set(1, 2, 3, 4)

scala> s.contains("x")
res0: Boolean = false

scala> val a: Set[Any] = s
a: Set[Any] = Set(1, 2, 3, 4)