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"))Scalaは正しいタイプのSetを推測できるように見えるため、2.10 でもコンパイルされることにも注意してください。ただし、暗黙的な変換では機能しません(stackoverflow.com/questions/23274033/…)。
LP_ 2014

回答:


55

Set関数としてのセットの背後にある概念のため、型パラメーターは不変です。次のシグネチャは、物事を少し明確にする必要があります:

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

Set共変である場合、関数の反変によりAapplyメソッドはタイプのパラメータを取得できませんASetでは潜在的に反変である可能性がありますAが、これも次のようなことをしたいときに問題を引き起こします。

def elements: Iterable[A]

つまり、最善の解決策は、不変のデータ構造であっても、物事を不変に保つことです。immutable.Map型パラメーターの1つでも不変であることがわかります。


4
この議論は「関数としての集合の背後にある概念」に関連していると思います-これは拡張できますか?たとえば、「関数としてのセット」には、「コレクションとしてのセット」にはない利点があります。その共変タイプの使用を失う価値はありますか?
oxbow_lakes 2009年

23
型シグネチャはやや弱い例です。セットの「適用」は、containsメソッドと同じです。悲しいかな、Scalaのリストは共変であり、containsメソッドも持っています。もちろん、リストの署名は異なりますが、メソッドはセットの署名と同じように機能します。したがって、設計上の決定を除いて、Setが共変であることを実際に妨げるものはありません。
ダニエルC.ソブラル

6
セットは、数学的な観点からはブール関数ではありません。セットは、いくつかの包含関数によって削減されないツェルメロ-フレンケル公理​​から「構築」されます。これの背後にある理由は、ラッセルのパラドックスです:セットのメンバーになることができるものがある場合、それ自体のメンバーではないセットのセットRを検討してください。次に、質問をします。RはRのメンバーですか?
oxbow_lakes 2010年

12
共分散を犠牲にすることがセットにとってそれだけの価値があると私はまだ確信していません。確かに、それが述語であることはいいことですが、通常は少しだけ冗長にして、「set」ではなく「set.contains」を使用できます(とにかく、多くの場合、「set.contains」は多くの場合に読みやすくなります)。
Matt R

4
リストのが含まれているための方法は、どれを取る、ないA.種類の:@Martin List(1,2,3).contains _である(Any) => Boolean、のタイプは一方です。Set(1,2,3).contains _res1: (Int) => Boolean
Seth Tisue

52

http://www.scala-lang.org/node/9764マーティン・オーダーズキーは書いています:

「セットの問題については、非分散は実装からも生じていると思います。一般的なセットは、キータイプの非可変配列であるハッシュテーブルとして実装されます。これは、少し厄介な不規則性であることに同意します。」

それで、これの原理的な理由を構築するための私たちのすべての努力は間違っていました:-)


1
しかし、いくつかのシーケンスも配列で実装されており、それでもSeq共変です...何か不足していますか?
LP_ 2014

4
これは、Array[Any]内部に保存することで簡単に解決できます。
2015年

@rightfoldは正しいです。合理的な理由があるかもしれませんが、これはそうではありません。
ポールドレイパー、2015年

6

編集:なぜこの回答が少し話題から外れているように思われるのか疑問に思う方のために、これは私(質問者)が質問を変更したためです。

Scalaの型推論は、状況によっては文字列ではなくCharSequencesが必要であることを理解するのに十分です。特に、2.7.3では次のように機能します。

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

immutable.HashSetを直接作成する方法については、作成しないでください。実装の最適化として、5要素未満のimmutable.HashSetは実際にはimmutable.HashSetのインスタンスではありません。それらは、EmptySet、Set1、Set2、Set3、またはSet4のいずれかです。これらのクラスはimmutable.Setをサブクラス化しますが、immutable.HashSetはサブクラス化しません。


あなたが正しいです; 実際の例を単純化しようとして、私は些細な間違いを犯しました:-(
oxbow_lakes 2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.