Example8.2

「Example8.2」の編集履歴(バックアップ)一覧はこちら

Example8.2」(2011/02/24 (木) 08:42:05) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

#co(){ 8.2 Variance Annotations The combination of type parameters and subtyping poses some interesting questions. For instance, should Stack[String] be a subtype of Stack[AnyRef]? Intuitively, this seems OK, since a stack of Strings is a special case of a stack of AnyRefs. More generally, if T is a subtype of type S then Stack[T] should be a subtype of Stack[S]. This property is called co-variant subtyping. } ** 8.2 変位指定アノテーション (Variance Annotations) 型パラメータとサブタイプ化の組み合わせは興味深い問題を引き起こします。たとえば、Stack[String] は Stack[AnyRef] のサブタイプであるべきでしょうか? 直感的には OK のように思われます。String のスタックは AnyRef のスタックの特別な場合だからです。より一般的に言うと、もし T が型 S のサブタイプなら、Stack[T] は Stack[S] のサブタイプであるべきです。この性質は&bold(){共変}(co-variant)サブタイプ化と呼ばれます。 #co(){ In Scala, generic types have by default non-variant subtyping. That is, with Stack defined as above, stacks with different element types would never be in a subtype relation. However, we can enforce co-variant subtyping of stacks by changing the first line of the definition of class Stack as follows. } Scala では、ジェネリック型はデフォルトでは非変(non-variant)サブタイプ化です。それは、前に定義した Stack については、異なる要素型のスタックは決してサブタイプ関係にないということです。しかし、クラス Stack の定義の一行目を次のように変更すれば、共変サブタイプ化を強制できます。 class Stack[+A] { #co(){ Prefixing a formal type parameter with a + indicates that subtyping is covariant in that parameter. Besides +, there is also a prefix - which indicates contra-variant subtyping. If Stack was defined class Stack[-A] ..., then T a subtype of type S would imply that Stack[S] is a subtype of Stack[T] (which in the case of stacks would be rather surprising!). } 形式上の型パラメータの前に + を置くことで、このパラメータについてサブタイプ化が共変であることを示します。+ に加えて、反変サブタイプ化を示す接頭辞 - もあります。もし Stack を class Stack [-T] ... と定義すれば、T が型 S のサブタイプなら、Stack[S] は Stack[T] のサブタイプになります (スタックの場合には驚くべきことでしょう!) 。 #co(){ In a purely functional world, all types could be co-variant. However, the situation changes once we introduce mutable data. Consider the case of arrays in Java or .NET. Such arrays are represented in Scala by a generic class Array. Here is a partial definition of this class. } 純粋関数的な世界では、すべての型は共変となることができます。しかし、もしミュータブル(変更可能)なデータを導入すると状況は変わります。Java や .NET の配列について考えてみましょう。そのような配列は、Scala ではジェネリックなクラス Array で表現されます。次はこのクラスの定義の一部です。 class Array[A] { def apply(index: Int): A def update(index: Int, elem: A) } #co(){ The class above defines the way Scala arrays are seen from Scala user programs. The Scala compiler will map this abstraction to the underlying arrays of the host system in most cases where this possible. } このクラスは、Scala のユーザープログラムから Scala の配列がどのように見えるかを定義します。Scala コンパイラはこの抽象化を、基礎となっているホストシステムの配列へ、それが可能なほとんどの場合、マップします。 #co(){ In Java, arrays are indeed covariant; that is, for reference types T and S, if T is a subtype of S, then also Array[T] is a subtype of Array[S]. This might seem natural but leads to safety problems that require special runtime checks. Here is an example: } 実際 Java では配列は共変です。つまり、参照型 T と S について、もし T が S のサブタイプなら、Array[T] は Array[S] のサブタイプです。これは自然なことに思えるかもしれませんが、特別な実行時チェックを必要とする安全上の問題をもたらします。次がその例です。 val x = new Array[String](1) val y: Array[Any] = x y(0) = new Rational(1, 2) // これは次の糖衣構文 // y.update(0, new Rational(1, 2)) #co(){ In the first line, a new array of strings is created. In the second line, this array is bound to a variable y, of type Array[Any]. Assuming arrays are covariant, this is OK, since Array[String] is a subtype of Array[Any]. Finally, in the last line a rational number is stored in the array. This is also OK, since type Rational is a subtype of the element type Any of the array y. We thus end up storing a rational number in an array of strings, which clearly violates type soundness. } 最初の行で、新しい文字列配列が生成されます。二行目で、この配列は Array[Any] 型の変数 y に束縛されます。配列が共変だということを仮定すると、これは OK です。なぜなら Array[String] は Array[Any] のサブタイプだからです。最後に、最終行で有理数がこの配列に代入されます。これも OK です。なぜなら型 Rational は、配列 y の要素型 Any のサブタイプだからです。そして結局最後には、文字列配列に有理数が格納され、明らかに型健全性を破ります。 #co(){ Java solves this problem by introducing a run-time check in the third line which tests whether the stored element is compatible with the element type with which the array was created. We have seen in the example that this element type is not necessarily the static element type of the array being updated. If the test fails, an ArrayStoreException is raised. } Java はこの問題を、三行目に実行時チェックを導入して、格納された要素と配列が作られた要素型との互換性をテストし、解決します。例で見たようにこの要素型は、必ずしも更新される配列の静的な要素型ではありません。テストが失敗すると ArrayStoreException が発生します。 #co(){ Scala solves this problem instead statically, by disallowing the second line at compile-time, because arrays in Scala have non-variant subtyping. This raises the question how a Scala compiler verifies that variance annotations are correct. If we had simply declared arrays co-variant, how would the potential problem have been detected? } Scala はその代わりに、この問題を静的に解決します。Scala では、配列は非変サブタイプ化なのでコンパイル時に二行目を許可しないことで、解決します。これは、Scala はどうやって変位指定アノテーションの正しさを検証するのか、という疑問を生じさせます。もし単に配列を共変に宣言したら、どうやって潜在的な問題を見つけるのでしょうか? #co(){ Scala uses a conservative approximation to verify soundness of variance annotations. A covariant type parameter of a class may only appear in co-variant positions inside the class. Among the co-variant positions are the types of values in the class, the result types of methods in the class, and type arguments to other covariant types. Not co-variant are types of formal method parameters. Hence, the following class definition would have been rejected } Scala は変位指定アノテーションの健全性を検証するために、用心深い推定をします。クラスの共変的型パラメータは、クラス内の共変な場所にだけ現れることができます。クラス内の値の型で共変な場所は、クラス内のメソッドの結果型と他の共変的型への型パラメータです。メソッドの形式的な引数の型は共変ではありません。したがって次のクラス定義は却下されます。 class Array[+A] { def apply(index: Int): A def update(index: Int, elem: A) ^ 共変的型パラメータ A が 反変的位置に現れる。 } #co(){ So far, so good. Intuitively, the compiler was correct in rejecting the update procedure in a co-variant class because update potentially changes state, and therefore undermines the soundness of co-variant subtyping. } これまでのところは問題ありません。直感的には共変的なクラスの update 手続きを却下する点でコンパイラは正しいです。なぜなら update は状態を変更することができ、したがって共変サブタイプ化の健全性の土台を揺るがすからです。 #co(){ However, there are also methods which do not mutate state, but where a type parameter still appears contra-variantly. An example is push in type Stack. Again the Scala compiler will reject the definition of this method for co-variant stacks. } しかしながら、状態を変更しないメソッドで、型パラメータが反変的な位置に現れるものがあります。例は型 Stack の push です。Scala コンパイラは共変的スタックに対するこのメソッド定義を再び却下します。 class Stack[+A] { def push(x: A): Stack[A] = ^ 共変的型パラメータ A が 反変的位置に現れる。 #co(){ This is a pity, because, unlike arrays, stacks are purely functional data structures and therefore should enable co-variant subtyping. However, there is a a way to solve the problem by using a polymorphic method with a lower type parameter bound. } これは残念です。なぜなら配列と違ってスタックは純粋関数的なデータ構造であり、共変サブタイプ化できるべきだからです。しかしこの問題を、下限境界付きの型パラメータをもつ多相的メソッドで解決する方法があります。 #center(){[[前ページ>Example8.1]] [[ 8 章>Chapter 8 Generic Types and Methods]] [[目次>ScalaByExample和訳]] [[次ページ>Example8.3]]} ---- #comment
#co(){ 8.2 Variance Annotations The combination of type parameters and subtyping poses some interesting questions. For instance, should Stack[String] be a subtype of Stack[AnyRef]? Intuitively, this seems OK, since a stack of Strings is a special case of a stack of AnyRefs. More generally, if T is a subtype of type S then Stack[T] should be a subtype of Stack[S]. This property is called co-variant subtyping. } #setmenu2(ex-r-menu) ** 8.2 変位指定アノテーション (Variance Annotations) 型パラメータとサブタイプ化の組み合わせは興味深い問題を引き起こします。たとえば、Stack[String] は Stack[AnyRef] のサブタイプであるべきでしょうか? 直感的には OK のように思われます。String のスタックは AnyRef のスタックの特別な場合だからです。より一般的に言うと、もし T が型 S のサブタイプなら、Stack[T] は Stack[S] のサブタイプであるべきです。この性質は&bold(){共変}(co-variant)サブタイプ化と呼ばれます。 #co(){ In Scala, generic types have by default non-variant subtyping. That is, with Stack defined as above, stacks with different element types would never be in a subtype relation. However, we can enforce co-variant subtyping of stacks by changing the first line of the definition of class Stack as follows. } Scala では、ジェネリック型はデフォルトでは非変(non-variant)サブタイプ化です。それは、前に定義した Stack については、異なる要素型のスタックは決してサブタイプ関係にないということです。しかし、クラス Stack の定義の一行目を次のように変更すれば、共変サブタイプ化を強制できます。 class Stack[+A] { #co(){ Prefixing a formal type parameter with a + indicates that subtyping is covariant in that parameter. Besides +, there is also a prefix - which indicates contra-variant subtyping. If Stack was defined class Stack[-A] ..., then T a subtype of type S would imply that Stack[S] is a subtype of Stack[T] (which in the case of stacks would be rather surprising!). } 形式上の型パラメータの前に + を置くことで、このパラメータについてサブタイプ化が共変であることを示します。+ に加えて、反変サブタイプ化を示す接頭辞 - もあります。もし Stack を class Stack [-T] ... と定義すれば、T が型 S のサブタイプなら、Stack[S] は Stack[T] のサブタイプになります (スタックの場合には驚くべきことでしょう!) 。 #co(){ In a purely functional world, all types could be co-variant. However, the situation changes once we introduce mutable data. Consider the case of arrays in Java or .NET. Such arrays are represented in Scala by a generic class Array. Here is a partial definition of this class. } 純粋関数的な世界では、すべての型は共変となることができます。しかし、もしミュータブル(変更可能)なデータを導入すると状況は変わります。Java や .NET の配列について考えてみましょう。そのような配列は、Scala ではジェネリックなクラス Array で表現されます。次はこのクラスの定義の一部です。 class Array[A] { def apply(index: Int): A def update(index: Int, elem: A) } #co(){ The class above defines the way Scala arrays are seen from Scala user programs. The Scala compiler will map this abstraction to the underlying arrays of the host system in most cases where this possible. } このクラスは、Scala のユーザープログラムから Scala の配列がどのように見えるかを定義します。Scala コンパイラはこの抽象化を、基礎となっているホストシステムの配列へ、それが可能なほとんどの場合、マップします。 #co(){ In Java, arrays are indeed covariant; that is, for reference types T and S, if T is a subtype of S, then also Array[T] is a subtype of Array[S]. This might seem natural but leads to safety problems that require special runtime checks. Here is an example: } 実際 Java では配列は共変です。つまり、参照型 T と S について、もし T が S のサブタイプなら、Array[T] は Array[S] のサブタイプです。これは自然なことに思えるかもしれませんが、特別な実行時チェックを必要とする安全上の問題をもたらします。次がその例です。 val x = new Array[String](1) val y: Array[Any] = x y(0) = new Rational(1, 2) // これは次の糖衣構文 // y.update(0, new Rational(1, 2)) #co(){ In the first line, a new array of strings is created. In the second line, this array is bound to a variable y, of type Array[Any]. Assuming arrays are covariant, this is OK, since Array[String] is a subtype of Array[Any]. Finally, in the last line a rational number is stored in the array. This is also OK, since type Rational is a subtype of the element type Any of the array y. We thus end up storing a rational number in an array of strings, which clearly violates type soundness. } 最初の行で、新しい文字列配列が生成されます。二行目で、この配列は Array[Any] 型の変数 y に束縛されます。配列が共変だということを仮定すると、これは OK です。なぜなら Array[String] は Array[Any] のサブタイプだからです。最後に、最終行で有理数がこの配列に代入されます。これも OK です。なぜなら型 Rational は、配列 y の要素型 Any のサブタイプだからです。そして結局最後には、文字列配列に有理数が格納され、明らかに型健全性を破ります。 #co(){ Java solves this problem by introducing a run-time check in the third line which tests whether the stored element is compatible with the element type with which the array was created. We have seen in the example that this element type is not necessarily the static element type of the array being updated. If the test fails, an ArrayStoreException is raised. } Java はこの問題を、三行目に実行時チェックを導入して、格納された要素と配列が作られた要素型との互換性をテストし、解決します。例で見たようにこの要素型は、必ずしも更新される配列の静的な要素型ではありません。テストが失敗すると ArrayStoreException が発生します。 #co(){ Scala solves this problem instead statically, by disallowing the second line at compile-time, because arrays in Scala have non-variant subtyping. This raises the question how a Scala compiler verifies that variance annotations are correct. If we had simply declared arrays co-variant, how would the potential problem have been detected? } Scala はその代わりに、この問題を静的に解決します。Scala では、配列は非変サブタイプ化なのでコンパイル時に二行目を許可しないことで、解決します。これは、Scala はどうやって変位指定アノテーションの正しさを検証するのか、という疑問を生じさせます。もし単に配列を共変に宣言したら、どうやって潜在的な問題を見つけるのでしょうか? #co(){ Scala uses a conservative approximation to verify soundness of variance annotations. A covariant type parameter of a class may only appear in co-variant positions inside the class. Among the co-variant positions are the types of values in the class, the result types of methods in the class, and type arguments to other covariant types. Not co-variant are types of formal method parameters. Hence, the following class definition would have been rejected } Scala は変位指定アノテーションの健全性を検証するために、用心深い推定をします。クラスの共変的型パラメータは、クラス内の共変な場所にだけ現れることができます。クラス内の値の型で共変な場所は、クラス内のメソッドの結果型と他の共変的型への型引数です。形式上のメソッドパラメータの型は共変ではありません。したがって次のクラス定義は却下されます。 class Array[+A] { def apply(index: Int): A def update(index: Int, elem: A) ^ 共変的型パラメータ A が 反変的位置に現れる。 } #co(){ So far, so good. Intuitively, the compiler was correct in rejecting the update procedure in a co-variant class because update potentially changes state, and therefore undermines the soundness of co-variant subtyping. } これまでのところは問題ありません。直感的には共変的なクラスの update 手続きを却下する点でコンパイラは正しいです。なぜなら update は状態を変更することができ、したがって共変サブタイプ化の健全性の土台を揺るがすからです。 #co(){ However, there are also methods which do not mutate state, but where a type parameter still appears contra-variantly. An example is push in type Stack. Again the Scala compiler will reject the definition of this method for co-variant stacks. } しかしながら、状態を変更しないメソッドで、型パラメータが反変的な位置に現れるものがあります。例は型 Stack の push です。Scala コンパイラは共変的スタックに対するこのメソッド定義を再び却下します。 class Stack[+A] { def push(x: A): Stack[A] = ^ 共変的型パラメータ A が 反変的位置に表れる。 #co(){ This is a pity, because, unlike arrays, stacks are purely functional data structures and therefore should enable co-variant subtyping. However, there is a a way to solve the problem by using a polymorphic method with a lower type parameter bound. } これは残念です。なぜなら配列と違ってスタックは純粋関数的なデータ構造であり、共変サブタイプ化できるべきだからです。しかしこの問題を、下限境界付きの型パラメータをもつ多相的メソッドで解決する方法があります。 #center(){[[前ページ>Example8.1]] [[ 8 章>Chapter 8 Generic Types and Methods]] [[目次>ScalaByExample和訳]] [[次ページ>Example8.3]]} ---- #comment

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。