Javaでアレっぽい何か

前回に引き続き復習を兼ねてJavaで遊ぶ。
もちろん今回もJava6です。

カリー化

カリー化の基本的なアイデアは「引数 x と y を受け取り式 e を計算する関数」を「x を受 け取ると『y を受け取って e を計算する関数』を返す関数」として表現することです。

プログラミング OCaml p60より

これを初めて読んだ時は何を言っているんだお前は状態になりましたが、特別難しい事を言っているわけではないのでそれっぽいことをやってみます。

2引数取りたいので前回のF1に加えてF2を作ります。

public abstract class F1<A, B> {
    public abstract B apply(A a);
}
public abstract class F2<A, B, C> {
    public abstract C apply(A a, B c);
}

1つ引数を受け取って何かを返す関数っぽいメソッドと、2つ引数を受け取って何かを返す関数っぽいメソッドを持った抽象クラスです。

このままだとF2のapplyは引数を2つ受け取って何かを返すメソッドなわけですが、[引数を1つ受け取って{引数を1つ受け取って何かを返すメソッド}を返すメソッド]を振るまいに追加します。

public abstract class F2<A, B, C> {
    public abstract C apply(A a, B b);

    public F1<A, F1<B, C>> curry() {
        final F2<A, B, C> f2 = this;
        return new F1<A, F1<B, C>>() {
            public F1<B, C> apply(final A a) {
                return new F1<B, C>() {
                    public C apply(final B b) {
                        return f2.apply(a, b);
                    }
                };
            }
        };
    }
}

だいぶ中身が気持ち悪い感じですが、これで[引数を1つ受け取って{引数を1つ受け取って何かを返すメソッド}を返すメソッド]を定義でた気がするので使ってみます。

    public static void main(String[] args) {
        F2<Integer, Integer, Integer> add = new F2<Integer, Integer, Integer>() {
            @Override
            public Integer apply(Integer a, Integer b) {
                return a + b;
            }
        };

        F1<Integer, Integer> add5 = add.curry().apply(5);

        System.out.println(add5.apply(20)); //25
        System.out.println(add.curry().apply(10).apply(20));
    }

addという関数っぽい何かを使ってadd5という関数を新たに簡単に作れちゃいました。わーい。
下の例だと流れている気がするメソッドチェーンができています。はっぴー。

Javaでやるもんじゃないですね…。