Groovyの「静的型付け」を引き続き考える
前の記事に書きましたが、id:kskyさんが記事「Groovyの「静的型付け」はホンモノか!?」でも考察されているように、Groovyの「静的型付け」は、Javaのようなコンパイル言語でのそれとは違った意味合いがあります。
その違いを細かく考えると、静的型付けには、以下の2つの意味(段階)があるということのようです。
- コード中の任意の時点での任意の式が,どの型であるかをコードを見ただけで(静的に)判別可能かどうか(静的型)
- 上記に加えて、型エラーを含んだコードをコンパイル時にチェックできるかどうか(静的な型チェック)
前者だけを満たしているのがGroovyの「静的型付け」で、両方を満たしているのがJavaとかになる、ということかと。なぜGroovyではコンパイルチェックできないかというと、asType()がMOP機能によって注入されている可能性があるので、型エラーかどうかが静的に判断できない(実行経路によっては注入されているかもしれない)からです。つまりこれは「動的言語」である事の代償です(動的言語!=動的型言語ですのでよろ)。
前者について、もうちょっと詳しく言うと、例えばGroovyで、
int i = ....
と書いたとき、このコード以降でiがint型以外のデータを指さない*1ことは静的に確定しています。もし
int i = "hello"
と書いたときでも、例外が発生する*2からです。
問題が型チェックのタイミングだけ*3だとすると、Groovyの場合はいずれにせよ大差が無いともいえます。Groovyのスクリプトは、基本的に実行時にコンパイルするものだからです。emacsとかviとかでGroovyスクリプトを書いている人には、それほどシリアスな問題になってこないのはこれが理由です。コンパイル時と実行時の時間的な差が1秒とかそれぐらいしかないので、前倒しの利点が実に小さいのです。仮にコンパイル時にチェックできたとしても、そんなに嬉しくもない。静的型のメリットは、大半享受できている事になります。
逆に、この違いが顕著になるのは、「ソースを保存したときにコンパイルしてくれる」という機能を持ったIDEを使う場合や、「ビルド」「統合」というフェイズを含んだ開発体系を採用する場合です。コンパイルと実行の時間差(もしくは場所/担当者の違い)が大きい場合に、「エラーチェックの前倒し効果」が効いてきて、コンパイル時型チェックの利点が拡大されていきます*4。Groovyの型指定は、この観点には全く寄与しないので、「カザリ」に過ぎない*5。
Groovyの評価が、人によって大きくわかれるように見えることの1つの理由がここにあると思います。人によってというより、用途・体制によるのです。
これはかなり根源的な問題ですが、コンパイル時チェックの利点をあくまで望む人のための改善策を願望として書いておくと、「GrUnitのインラインテスト」とかで、Groovyコード中に正常系テストケースを1つ書いておくようにしておいて、groovycをかけたときに、自動テスト実行してくれて、そこで発生する型エラーを特にあたかも「コンパイルエラーの様に」表示してくれるようになるといいんじゃないかなあ。もちろんこれは、エディタとかIDEが、「保存時コンパイル」をgroovycを使ってやってくれるという前提です。これによって、基本的な型エラーが、コードの保存時に特定箇所のエラーメッセージとして表示されるだけでも、開発効率の印象がずいぶん違ってくると思います。これはスクリプト派の人であってすら*6、大きな利点になるのではないでしょうか。デメリットも出てくるかもしれませんが。
そこまでするなら、普通に静的言語(scalaとか)を使えよ、と思うかもしれませんが、私の考えではそこは「オプショナルであるべき」ということです。静的型・動的型の両方を部分的に使いつつ、都合に応じて細粒度で選べ、シームレスに自在に相互移行もできるような解があるといいなと。
もちろん、Groovy++もいいですね。