Scala Set,不变类型发生了什么?
Scala Set, what is happening with invariant types?
在对 Scala 应用程序进行重构时,我遇到了从 List 更改为 Set 的情况,这引发了一个我以前没有遇到过的问题。我对方差有一些了解,但我想了解它对编译器到底意味着什么。
我有类似的东西,它编译并工作得很好:
case class MyClassList(s: List[Any])
val myList = List(("this", false)) // List[(String, Boolean)]
val listWorks = MyClassList(myList)
然后我将列表更改为设置:
case class MyClassSet(s: Set[Any])
val mySet = Set(("this", false)) // Set[(String, Boolean)]
val setFails = MyClassSet(mySet)
在这一点上,创建一个 MyClassSet 类型的对象不再适合我传递一个 Set 作为参数,即使它接受一个 Set of Any。现在,当以下工作时它有点混乱(请注意该集合是 "the same" 与之前的 mySet 一样):
val setWorks1 = MyClassSet(Set(("this", false)))
我相信简单的解释是编译器将 mySet val 推断为一个 Set[(String, Boolean)],但是当我直接在 setWorks1 的参数列表中实例化它时,因为它接受一个 Set[Any ],编译器将其推断为 Set[Any]。这使得第一个示例失败而第二个示例通过。这些也有效,这表明前一个是正确的:
val setWorks2 = MyClassSet(mySet.toSet[Any])
val mySetOfAny: Set[Any] = Set(("this", false), ("that", true), ("other", false))
val setWorks3 = MyClassSet(mySetOfAny)
编译器显示的实际错误是:
Error:(15, 55) type mismatch;
found : Set[(String, Boolean)]
required: Set[Any]
Note: (String, Boolean) <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: (...)
列表和集合定义如下:
type List[+A] = scala.collection.immutable.List[A]
type Set[A] = immutable.Set[A]
- 这种类型差异是否允许我将 "more restricted type than Any" 的列表作为参数传递,但在
设置?
- 这种差异是否只是阻止了类型之间的转换或转换?
- 这主要是编译器 "limitation" 还是预期的 属性 不变类型?
- 不变类型之间还有其他区别吗 "in practice" 或者它们是否归结为这样的转换?
1) 在这里解释:
Why is Scala's immutable Set not covariant in its type?
基本上,Set[T]也是一个Function1[T, Boolean]
。 Function1
的签名是 [-In, +Out]
,所以 T
不能同时是 +T
和 -T
,因为 scala 不允许双方差(它会显着削弱类型系统)。
2) 您可以使用 .toSet[Any]
(它是 asInstanceOf
的包装器)轻松转换它。还有一种方法可以skip variance check.
3, 4) 预计 属性 是泛型(多态)类型。它们可以是 invariant/covariant/contravariant(不仅如此),而且它由简单的规则正式描述。你可以在这里阅读解释:
在对 Scala 应用程序进行重构时,我遇到了从 List 更改为 Set 的情况,这引发了一个我以前没有遇到过的问题。我对方差有一些了解,但我想了解它对编译器到底意味着什么。
我有类似的东西,它编译并工作得很好:
case class MyClassList(s: List[Any])
val myList = List(("this", false)) // List[(String, Boolean)]
val listWorks = MyClassList(myList)
然后我将列表更改为设置:
case class MyClassSet(s: Set[Any])
val mySet = Set(("this", false)) // Set[(String, Boolean)]
val setFails = MyClassSet(mySet)
在这一点上,创建一个 MyClassSet 类型的对象不再适合我传递一个 Set 作为参数,即使它接受一个 Set of Any。现在,当以下工作时它有点混乱(请注意该集合是 "the same" 与之前的 mySet 一样):
val setWorks1 = MyClassSet(Set(("this", false)))
我相信简单的解释是编译器将 mySet val 推断为一个 Set[(String, Boolean)],但是当我直接在 setWorks1 的参数列表中实例化它时,因为它接受一个 Set[Any ],编译器将其推断为 Set[Any]。这使得第一个示例失败而第二个示例通过。这些也有效,这表明前一个是正确的:
val setWorks2 = MyClassSet(mySet.toSet[Any])
val mySetOfAny: Set[Any] = Set(("this", false), ("that", true), ("other", false))
val setWorks3 = MyClassSet(mySetOfAny)
编译器显示的实际错误是:
Error:(15, 55) type mismatch;
found : Set[(String, Boolean)]
required: Set[Any]
Note: (String, Boolean) <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: (...)
列表和集合定义如下:
type List[+A] = scala.collection.immutable.List[A]
type Set[A] = immutable.Set[A]
- 这种类型差异是否允许我将 "more restricted type than Any" 的列表作为参数传递,但在 设置?
- 这种差异是否只是阻止了类型之间的转换或转换?
- 这主要是编译器 "limitation" 还是预期的 属性 不变类型?
- 不变类型之间还有其他区别吗 "in practice" 或者它们是否归结为这样的转换?
1) 在这里解释: Why is Scala's immutable Set not covariant in its type?
基本上,Set[T]也是一个Function1[T, Boolean]
。 Function1
的签名是 [-In, +Out]
,所以 T
不能同时是 +T
和 -T
,因为 scala 不允许双方差(它会显着削弱类型系统)。
2) 您可以使用 .toSet[Any]
(它是 asInstanceOf
的包装器)轻松转换它。还有一种方法可以skip variance check.
3, 4) 预计 属性 是泛型(多态)类型。它们可以是 invariant/covariant/contravariant(不仅如此),而且它由简单的规则正式描述。你可以在这里阅读解释: