kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Javaのジェネリクスは「まがい物」ではない

先日、自分が書いた

kmizu.hatenablog.com

に対する反応として、「Javaのようなまがい物のジェネリクスと比較するのは適切でない」「Javaジェネリクスと比較するのは適切でない」(おそらくC#や(C++等(2017/09/24追記))の言語と比較して)といった コメントをいくつか見かけました(はてなブックマークコメントやツイッターなどで)。しかし、ここでは、そのような言説こそが適切でない、ということを言いたいです。なお、methane氏との 対話については既に終わったものなので、それとは関係ありません。

そもそも、Javaジェネリクスは、静的型付き関数型言語で既に一般的であったパラメータ多相をJavaに追加する目的で導入されました(C++テンプレートのようなものをJavaに追加するためだと思っている人がいるかもしれませんが、それは実態にあっていません)。実際、Java Genericsの前身のGeneric Javaのさらに前身であるPizzaの論文で、Martin Odersky*1は、PizzaはJavaにパラメータ多相(parametric polymorphism)などを導入するものであることを明言しています。

この、パラメータ多相という用語は、型理論の分野で広く使われている用語で、型によってコードをパラメータ化できるという性質を指します。重要なのは、パラメータ多相(ジェネリクスと同義)では、そのような、型によってパラメータ化されたコードを表現できて、なおかつその整合性を静的にチェックできるという点が本質であって、 型パラメータが実行時に取得できる とか、 特定の型引数に対して特殊化できる とかいった性質は、あくまで 実装方式 によって得られる追加のメリットであるということです。そして、追加のメリットがないというだけで、Javaジェネリクスが紛い物だとか主張するのは、JavaC#、あるいはJavaC++とかしか知らない人が自らの無理解を示しているに過ぎないし、噴飯モノの言説だということです。

また、実行時に型パラメータ情報を持ったり、特定の型パラメータに関して特殊化できるというのは便利ですが、ランタイムの実装を大幅に複雑化します(実行時にもパラメータ多相を扱えなければいけないので)。一方で、実行時に型パラメータ情報を持たなければ、ランタイムの型システムをある程度無視して言語の型システムを拡張しやすいです(Scalaが典型例)。

別の例としては、Javaは2016年に、型システムが健全でないことが証明されたり型チェックが決定不能であることが証明されたりしていますが、ランタイムの型システムは別なので、これらの問題について、ランタイムが影響を受けることはありません。一方で、C#をはじめとした.NET言語では、言語の型システムとランタイムの型システムがかなり近いため、言語の型システムに欠陥が見つかると、それが直接ランタイムに影響を与えるおそれがあります。

このように、ジェネリクスの実装方式はそれぞれ得失があるので、どちらがいいとすぐ決めつけられるものではありません。両方の性質についてきちんと理解した上で、どちらの方式を支持するか決めるのはもちろんいいですが、その前に色々勉強すべきことがあると思います。

以下、2017/09/24追記

特定の型パラメータに関して特殊化できる、というのの意義が伝わっていなかったので書くと、

class G[T] {
  def puts(x: T) = println(x)
}

があったとして、

G[Int]

に対しては、

class G_Int{
  def puts(x: Int) = println(x)
}

のような効率的なコードが生成される(あるいはできる)ということを指しています。今回、別に実行時型情報のみを問題にしたわけではありません。

Javaのジェネリクスは「まがい物」ではない - kmizuの日記

RTTI(実行時型情報)は何の関係もないです。筆者は根本的なところで勘違いしていませんか?「Scalaジェネリクスだとforがインライン化できずに糞遅い」理由を考えてみましょう。JavaScalaのはただのCastのSugarです。

2017/09/24 10:58
b.hatena.ne.jp

上記の通り、特に勘違いしていません。また、Scalaforが遅い理由についても、何か勘違いされてると思います。キャストの構文糖衣だというような見方については、今回の本文を読んでくださいとしか。言語の型システムの話をしているのに、いきなり、実装としての、「キャストをはさむ」ことに言及されてるので、その二つの違いについてじっくり考えてみられると良いかと。

*1:Scalaの作者であり、GJ Compilerの作者