4

Scala言語仕様(分散注釈に関するセクション4.5、p。44)は言う

  • 型パラメーターの分散位置は、それを囲む型パラメーター句の分散位置の反対です。
  • 型宣言または型パラメーターの下限の分散位置は、型宣言または型パラメーターの分散位置の反対です。

上記の最初のポイントを使用すると、(少なくとも形式的には) 簡単にわかります。

trait Covariant[+A] {
  def problematic[B <: A](x : B)
}

エラーメッセージを生成します

error: covariant type A occurs in contravariant position in type >: Nothing <: A of type B
       def problematic[B <: A](x : B)

1 番目と 2 番目のポイントを使用すると、簡単に確認できます。

trait Contravariant[-A] {
  def problematic[B >: A](x : B)
}

エラーメッセージを生成します

error: contravariant type A occurs in covariant position in type >: A <: Any of type B
             def problematic[B >: A](x : B)

前述したように、これらのエラーが発生する理由を形式的に (つまり、分散注釈の規則に従って) 確認するのは簡単です。ただし、これらの制限の必要性を示す例を思いつくことはできません。対照的に、メソッド パラメータが分散位置を変更する必要がある理由を説明する例を考え出すのは非常に簡単です。たとえば、分散注釈のチェックを参照してください。

そこで、私の質問は次のとおりです。上記の 2 つのコードが許可されたと仮定すると、発生する問題の例は何ですか? つまり、上記の 2 つのルールが使用されなかった場合に何が問題になるかを示す、これと同様の例を探しています。型の下限に関する例に特に興味があります。

Scala の型の境界と分散に対する回答では、この特定の質問が未解決のままであることに注意してください。一方、 「下限」で与えられた回答は、型の分散を逆にしますが、なぜでしょうか? 私には間違っているようです。

編集:最初のケースは次のように処理できると思います(上記の例を適応させます)。以下が許可されたとします。

trait Queue[+T] {
  def head : T
  def tail :  Queue[T]
  def enqueue[U <: T](x : U) : Queue[T]
}

次に、実装できます

class QueueImplementation[+T] extends Queue[T] {
  /* ... implement Queue here ... */
}

class StrangeIntQueue extends QueueImplementation[Int] {
  override def enqueue[U <: Int](x : U) : Queue[Int] = {
    println(math.sqrt(x))
    super.enqueue(x)
  }
}

そしてそれを次のように使用します

val x : Queue[Any] = new StrangeIntQueue
x.enqueue("abc")

これは明らかに面倒です。ただし、「反変型パラメーター + 型下限」の組み合わせにも問題があることを示すために、これを適応させる方法がわかりませんか?

4

2 に答える 2

1

++からのメソッドを使用してList、制限が必要な理由を確認します。当然のことながら、これには以下を++生成する必要がありList[B]ます。

 def ++[B](that: GenTraversableOnce[B]): List[B] 

の完全な署名付き

 def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

では、なぜそれが重要なのでしょうか[B >: A]。さて、何かを組み合わせたいとしたらどうでしょうか

 trait Foo
 trait Bar extends Foo

シグネチャを持つメソッドがあります

 def op(that: List[Foo], other: Foo): List[Foo] = that ++ List(other)

タイプのリストを渡すことはできますがBar、それを として返すことができるようにするには、実際に次のことを実行できるようにするList[Foo]必要があります。Foo >: Bar

 def see(that: List[Bar]): List[Foo] = op(that, myFoo)

これは本質的に、型を介して表現されList[Bar] ++ List[Foo]た型を返すためにa を実行しています。それがフリップが起こる理由です。List[Foo]List[Foo]

ここで、タイプのリストを返すことができないFoo <: Barという問題にすぐに遭遇することを強制しようとすると(上記の定義と競合することは言うまでもありません) 、最小の上限の a しか返すことができません。List[Bar] ++ List[Foo]FooList

于 2014-05-30T16:32:41.283 に答える
1

[-T]クラスが型パラメータを持つことを許可し、そのクラスのメソッドが持つことを許可するとしましょう[U >: T]...

for come class hierarchy
Dog <: Mammal <: Animal

class Contra[-X](x: X){
  def problem[Y >: X](y: Y): Y = x // X<:Y so this would be valid
}

val cMammal:Contra[Mammal] = new Contra(new Mammal)

val a:Animal = cMammal problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cMammal problem new Mammal // Mammal >: Mammal, this is fine
val d:Mammal = cMammal problem new Dog    // (Dog upcasts to Mammal) >: Mammal, this is fine

val cDog:Contra[Dog] = cMammal // Valid assignment

val a:Animal = cDog problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cDog problem new Mammal // Mammal >: Mammal, this is fine
val d:Dog    = cDog problem new Dog    // AAAHHHHHHH!!!!!!

この最後の行は check と入力し、cDog problem new Dog実際にはMammal. これは明らかに良いことではありません。ありがたいことに、型システムでは実際にこれを行うことはできません。

QED 反変型パラメーター + 下限型境界を混在させるのは得策ではありません。

この例が役立つことを願っています。

于 2014-05-30T19:31:17.050 に答える