ジェネリクスのカリー化

関数を扱えるだけでは、モナドを表現するには不十分過ぎる - xuwei-k's blogを見て、言語の型システムの表現力とその限界とはなんなんだろうということをモヤモヤと考えていた。

ここで話題になっているのは型変数を持つ型変数という話題である。これを便宜的に高階型変数とか、高階型と表現している。Javaジェネリクスで例えれば

public class Foo<T<X>> {}

といったように型変数Xを持つ型変数Tという表現の話しである。Javaの言語機能的にはコレはできない。

以前、僕の書いたJavaによる高階型変数の実装 - プログラマーの脳みそではこの出来ないはずの高階型変数をJavaの言語機能で実現するにはどうしたらいいかという工学よりの話題で、型理論のような理論よりの話題としてはどう評価するべきなのかちょっとよくわからない。

Higher-kind Generics

ScalaのHigher-kind Genericsについて - TogetterではHigher-kind Generics ―― これを高階型変数と訳語をあてるのは適切かはさておき ―― についての解説をしていて以下に部分的に抜粋する。



「ある型Xを型パラメータとして取って何かの型を作り出すもの」(型コンストラクタと呼ばれる)という1段抽象度の高いモノが出てくる。

Javaの場合は言語機能としてはこのような概念はないわけだけども、この概念に即したことをなんらかの実装方法でシミュレーションすることが先の高階型変数の論理的な意味ということになるのだろうか。

ではどのように実現を試みるかという話なのだが、先の高階型変数のエントリでは内部クラスを利用するアプローチをとった。

Javaの内部クラスは、クラス-インスタンス関係のように、インスタンス-インスタンス関係を作ることができる。(内部クラスについてはJavaのクラス宣言5種+α - プログラマーの脳みそなどを参照されたし)これをHigher-kind Genericsでの型コンストラクタに見立てて高階型変数を表現しようとしたのだった。

内部クラスでの代替

Javaの内部クラスは外部クラスのインスタンスフィールドやメソッドにアクセスすることができるが、同等に親クラスの型変数を用いることもできる。

public class Outer<T1> {
	public class Inner {
		T1 t1;
	}
}

さらに、内部クラスは内部クラス自身に型変数の宣言を行うことができる

public class Outer<T1> {
	public class Inner<T2> {
		T1 t1;
		T2 t2;
	}
}

この内部クラスのインスタンス化は次のようなコードになる。

Outer<String> outer = new Outer<>();
Outer<String>.Inner<Integer> inner = outer.

ここで、外部クラスのインスタンスを作った時点では型変数T1のみバインドしている(ここの例ではStrign)ところに注目しよう。後からT2にバインド(ここの例ではInteger)して内部クラスのインスタンス生成が完了する。つまりインスタンス生成が2段階で行われることになる。

ここで、内部クラスをHashMap<T1, T2>の継承としてみよう。型変数をふたつ持つ馴染みのあるクラスとしてHashMapを選んでみた。

public class Outer<K> {
	public class Inner<V> extends HashMap<K, V> {}
}


このように、Innerは、型変数T2を渡すことでHashMap<T1, T2>を作ることができる。Innerを作るには型変数T1を与えることになる。これはあたかも型変数に対するカリー化のように見える。

型変数T1に対してバインドしてOuterのインスタンスを作ることは型変数の部分適用であるかのようだ。

Outer<String> o = new Outer<String>();
HashMap<String, Integer> inner = o.new Inner<Integer>();

上記例では明示的にバインドしているがダイヤモンドでバインド型を推論することもできる。

むすび

さて、ここまで見てきたように内部クラスを持つ型を使うことで型コンストラクタかのように扱えるように見える。Scalaにおける型コンストラクタの利用例をJavaで表現してみることで何が表現できて何が表現力不足なのかを論じることができるのではないかと思う。