読者です 読者をやめる 読者になる 読者になる

ファンクタがなければクロージャを食べればいいわ

Java Ruby Scala

次のJDK入ってたかもしれない「クロージャ」、見送りになってそれはそれで良かったんけど、そもそも、どういう仕様のものだったのか、いまごろ気になってきました。というのも、明らかにいまJavaの魅力は相対的に落ちてて、「面倒な安全装置のついた機械」より「一目で危険だとわかる単純明快な道具」のほうが安全に使えることがわかってきた時代に入ったと思うのです。Javaコミュニティがクロージャみたいなものに魅かれる気持ちはすごくわかるけど、一方で、安易にメモリ管理を複雑化させるのはJavaの価値を根元から絶ってしまうことにもなる。どこが妥協点なんだろう?と、ちょっと考えてみました。

Java関数型言語じゃないからファーストクラス関数がないのは当然。我慢しなさい、ってなもんだ。OOPに特化したVMなんだもん、二重人格なプラットフォームアーキテクチャは、むしろ、Javaを余計に使えなくしてしまう(ActionScript3とPHP5を見よ)。

なので、Javaではむかしから、関数を取り回す必要があるときは、クロージャもどきを備えたファンクタで補うことになってた。で、このめんどうなファンクタクラスの実装、自動的に定義してくれるように構文サポートが得られたらいいのになぁ。JDKにそこそこ使えるファンクタリテラルを設けたら、クロージャをサポートしなくていいかもしれない。この仮説、どうだろう?

たとえば、

public void foo() {
    int a = 0;
    int b = 0;
    //ここでファンクタを作ってfに保存したいな。じっさいはこんな文法ないけど。
    int (int c) f = new int (int c){ return a + b + c; };
    a = 1;
    b = 2;
    System.out.println(f.call(3));
}

と書いたら、勝手に、こんなふうに解釈してコンパイルしてくれないもんだろうか…。

class Variable<T> {
    private T value;
    public T get() { ... };
    public set(T value) { ... };
}

class AnnonymousFunctor {
    private Variable<Foo>  _v_this;
    private Variable<Integer> _v_a;
    private Variable<Integer> _v_b;
    public AnnonymousFunctor(
        Variable<Foo> _ref_this,
        Variable<Integer> _ref_a,
        Variable<Integer> _ref_b
    ) {
        _v_this = _ref_this;
        _v_a = _ref_a;
        _v_b = _ref_b;
    }
    public int call(int c) {
        return _v_a.get() + _v_b.get() + c;
    }
}

public void foo() {
    Variable<Integer> _v_a = new Variable<Integer>(0);
    Variable<Integer> _v_b = new Variable<Integer>(0);
    AnnonymousFunctor f = new AnnonymousFunctor(
        new Variable<Foo>(this), _v_a, _v_b);
    _v_a.set(1);
    _v_b.set(2);
    System.out.println(f.call(3));
}

う〜ん、やっぱりJavaはこういうことするような言語じゃない、のかな。せめて型制約がもっと弱ければ…、あ型制約?じゃあRubyだとどうなる?

def foo()
    a = 0
    b = 0
    f = lambda {|c| a + b + c}
    a = 1
    b = 2
    p f.call(3)
end

ああ、これこそJavaに求めたファンクタオブジェクトだ。クロージャ教に騙されることなく、RubyのProcクラスを目指せばいけそうだ。関数型じゃないからって、クロージャのすべてを諦める必要はない。Rubyはファーストクラス関数のない、根っからのOOP言語だけど、だからこそ、ファンクタの生成について強く意識されている。やっぱり、妥当に便利な言語だなと再認識。

やりすぎるとGroovyになっちゃう。ファンクタどころか、もろにファーストクラス関数をサポート。でもそこまでいくと、OOPなのか関数型なのか、どっちつかずになってVMのシンプルさがなくなるからダメ、なんか、Perl末期っぽい。GroovyはPerlっぽいダーティーさがいいから、それはそれでいいんだけど。

Groovyは行き過ぎなので、OOPと関数型をものすごく慎重に模索してるScalaで考えてみた。

def foo():Unit = {
    var a, b = 0
    val f = (c) => { a + b + c }
    a = 1
    b = 2
    println(f(3))
}

まあ、自然に書けばクロージャになるわな、やっぱり。ところで、これ、コンパイルして出てくるclassファイルが、なんだか最初のJavaの例みたいになるような気がするんだけど…。JVMではファンクタとして、でも、言語仕様ではクロージャとして扱えるなら、Rubyとは違うやりかたで行儀良さを守ってる(クールなコードを早く書けるだけが価値じゃないよね)といえる。ゴスリングさんが「Javaの次としてScalaはとても興味深い」と言ってたのは、これのことかな。低級言語であるJavaにコンパイルされる、未来の高級言語って考え方、うーんいいねえ。

補足:
Javaの言語構文のダサさや、スーパーPGでも生産性倍率の上限が知れている件はともかくとして、Javaには他の言語がどうしても担えない領域がある。JVMでは、管理単位がオブジェクトというシンプルなメモリブロックに収まっていて、それはレガシーかもしれないけど、何よりシンプルで、ガベージコレクションに関しては世界一枯れている。Javaは、「絶対に間違ってはいけないプログラム」を「書くことができる」言語なんだ。まあ、できる、ってだけで、平均的なプログラマでは「書けない」のが現実なんだけどね。

結局、RubyScalaに頼っちゃったけど、答えはそのへんにあるのかも。Javaだけを使って高級言語のように書きたいという欲求は、私が最初に思っていたイメージよりも、もっと根本的なレベルで間違っていることなのかもしれない。JRubyファンクタを食べてもいいし、Scalaクロージャを食べてもいい、それが許されないなら、腹が減ってても高級豆コーヒーを飲んで我慢するしかない(大丈夫、そのうち胃潰瘍になって食べられなくなるから)。