PR

Existential Type

 JavaのGenericsは共変/反変ではないと述べましたが,Javaでもワイルドカードという機能を使うことで,それに近い効果を得ることができます。ワイルドカードは,型を定義する側ではなく,使う側に付加するもので,型を使う個所で,G<? extends T1>や,G<? super T1>のようにして使います。

 前者の形式は共変に対応しており,T1のサブタイプである型T2に対して,G<T2>がG<? extends T1>のサブタイプになります。後者の形式は反変に対応しており,T1のスーパータイプである型T2に対して, G<T2>がG<? super T1>のサブタイプになります。そのため,次のようなコードがコンパイルを通ります。

java.util.List<String> s1 = ...; 
java.util.List<? extends Object> s2 = s1; //共変に対応
java.util.List<Object> s3 = ...;
java.util.List<? super String> s4 = s3; //反変に対応
...

 ワイルドカードは,型を使う側に付加するものであり,共変/反変な型の変数を宣言する必要があるたびに記述しなければならないため,煩雑であるという欠点がありますが,一方で,型の定義時点では共変/反変でないものを共変/反変であるものとして扱うことができるという利点もあります。

 Scalaでも,Existential Typeという機能を使うことで,ワイルドカードと同じことを実現することができます。例えば,上記のワイルドカードを使ったJavaのコードと同じことは,次のようなScalaのコードによって実現できます。

//java.util.List[_ <: Any]と略記することもできる
var s1: java.util.List[String]  = new java.util.ArrayList
var s2: java.util.List[T] forSome { type T <: Any } = s1 
//java.util.List[_ >: String]と略記することもできる
var s3: java.util.List[Any] = new java.util.ArrayList
var s4: java.util.List[T] forSome { type T >: String} = s3
...

Structural Types

 Javaのような言語では,あらかじめ宣言した型同士でのみサブタイプ関係が定義されます。例えば,Javaにおいて次のようなクラスA,B,Cの定義があったとします。

class A {
  void call() {}
}

class B extends A {
  void call() {}
}

class C {
  void call() {}
}

 このとき,void foo(A a)というメソッドがあったとして,fooの引数としてAおよびBのインスタンスを渡すことはできますが,Cのインスタンスを渡すことはできません(Cのインスタンスもcallメソッドを持っているのに!)。

 これは,クラスBはAとの関係をextends Aと明示することによって,Aとのサブタイプ関係ができているのに対して,クラスCはA(またはB)との関係を明示的に記述していないため,Aとのサブタイプ関係が存在しないからです。JavaやC#(C++はちょっと違う事情があるので除きます)などのメインストリームの静的型付けオブジェクト指向言語ユーザーにとっては,このことはあまりにも当然過ぎて,あえて意識したことが無い方も多いと思います。

 一方,動的型付け言語のコミュニティでは,duck typingと言って,クラスの継承関係がどうであろうが,オブジェクトに要求されるメソッドさえ持っていればOKという考え方が一般的です。先ほどの例で言えば,AもBもCもcallメソッドを持っているのだから,fooの中でaに対してcallメソッドしか呼び出されていないのならば,Cのインスタンスもfooの引数として渡すことができるということです。

 しばしば,duck typingは動的型付け言語では静的な型チェックが無いからこそ可能な技法であるかのように言われることがありますが,少し考えてみればわかる通り,静的型言語であっても,ある型がどのようなメソッドを持っているかは静的にわかるのですから,原理的には,静的型チェックを犠牲にせずに,要求されているメソッドの集まりを持った型であればなんでもOKというような型を表現することができるはずです。

 Scalaでは,Structural Typeという機能によって,そのような型を表現することができます。Structural Typeの使い方は単純で,型宣言で,{}で囲って持っているべきメソッドのシグニチャを列挙するだけです。例えば,callメソッドを持っている型を受け取るメソッドfooはScalaでは次のように宣言することができます。

def foo(callable: { def call: Unit }) = ...

 また,列挙するメソッドの数が多い場合などは,次のようにして別名を付けることで,毎回すべてのメソッドを列挙することなく,Structural Typeを使うことができます。

type Callable = { def call: Unit }

Compound Types

 Javaを使っているときに,「Aを継承しており,かつ,インタフェースBを実装した型」のような型を使いたくなったことは無いでしょうか?Javaでは,Genericsの型パラメータの制約を除いて,そのような型を記述することはできませんが,Scalaでは,そのような型を普通に記述することができます。使い方は簡単で,通常の型の後に,追加したい型をwithでつなげて書くだけです。

 例えば,以下の変数fには,java.io.CloseableインタフェースとReadableインタフェースの両方を実装しているオブジェクトならなんでも代入することができます。

var f: java.io.Closeable with Readable = ...

おわりに

 簡単ではありますが,Scalaの型システムに関する機能について紹介してみました。いかがでしたか?パス依存型など,この記事では紹介しきれていない,型システムに関連した話題もありますので,興味を持った方は,Scalaの言語仕様やScalaの公式Webページからダウンロードできる論文などを読んでみてはいかがでしょうか。