1

サブタイプを持つ Foo 型があります。

とりわけ Foo を含む別のクラスがあります。

class FooResult(val foo: Foo ...

FooResult のセットがあり、map() を使用してそこから Foo を取り出し、(Foo の) セットと別の Foo のセットとの差分を計算したいと考えています。以下の「結果」は Set[FooResult] であり、これが重要な部分です。バンドルは Set[_ <: Foo] です。

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

2 行目はコンパイルされません。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 のすばらしいコレクション フレームワークの弱点なのでしょうか (非常にありそうにないと思います)。

4

1 に答える 1

3

これは、Set.contains/apply をタイプセーフにするという意識的な設計上の決定です。as:Set[Int] を持つことはできず、誤って s.contains("x") のようなことを行うべきではありません。これは常に false になるため、意図したものではない可能性があります。また、Set[T] は Function[T,Boolean] を実装します。これは、apply メソッドが Any を取らない場合にのみ可能です。

scala-user メーリング リストでは、このトピックに関する無限の議論が行われました。たとえば、 thisまたはthisを参照してください。

以下は、理論的根拠を非常によくまとめた 2 番目のディスカッションからの Paul Phillips からの引用です。

「はい、apply (contains のエイリアス) は Set の中心的な操作ですが、contains は Seq の「単なるメソッド」です。

共変セットが必要ない場合があるとは誰も言っていませんが、全体として、それはより有用な不変セットです。」

暗黙的な変換を使用して、いつでもセットに共分散を追加できることに注意してください。これは、たとえば Set[Int] と Set[Any] を取るメソッドがある場合、それが機能することを意味します。しかし、これは、誤って Set[Int].contains("x") を呼び出す可能性があることも意味し、コンパイラはエラーをキャッチしません (常に false になります)。

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)
于 2013-06-27T22:03:49.187 に答える