Java7でカリー化?(部分適用でした、、、)

id:nowokayさんのJava 8を関数型っぽく使うためのおまじない - きしだのはてな記事を写経して、その後たまたまJava Magazineの1月号「Demystifing invokedynamic」記事を読んで写経して(最後のASM libraryを使っている記事は写経できてないですが)、これってカリー化かな〜と思ったのでちょっとメモとして書いてみました。コメントでも指摘いただいたように部分適用でした。

  • まず実行結果


上記の記事の中で2つ引数をとっている関数と、3つ引数をとっている関数が紹介されていたので同じように実装してみました。

    static String sandwich(String enc, String str) {
        return enc + str + enc;
    }
  • sandwichメソッドカリー化部分適用して実行

insertArgumentsを使う場合は、第2引数で固定化する引数を指定(ここでは第1引数なので「0」を指定)
bindToの場合は、第1引数を固定化

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        final MethodType type = MethodType.methodType(String.class, String.class, String.class);
        MethodHandle sandwich = lookup.findStatic(Sample.class, "sandwich", type);
        Object result1 = sandwich.invokeWithArguments("*", "sanded!");
        System.out.println(String.format("result1:%s", result1));
        // 「**」で囲むメソッドを作成、第1引数に「**」を指定
        MethodHandle sandwich2 = MethodHandles.insertArguments(sandwich, 0, "**");
        Object result2 = sandwich2.invokeWithArguments("sanded!");
        System.out.println(String.format("result2:%s", result2));
        // 「***」で囲むメソッドを作成、第1引数に「***」を指定
        MethodHandle sandwich3 = sandwich.bindTo("***");
        Object result3 = sandwich3.invokeWithArguments("sanded!");
        System.out.println(String.format("result3:%s", result3));
    static String encloseC(String pre, String post, String str){
        return pre + str + post;
    }
  • encloseCメソッドカリー化部分適用して実行

insertArgumentsを使う場合は、第2引数で固定化する引数を指定(ここでは第1引数なので「0」を指定)さらに第4引数で第2引数を指定
bindToの場合は、第1引数を固定化するので、再度bindToで第2引数を固定化

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType type = MethodType.methodType(String.class, String.class, String.class, String.class);
        MethodHandle encloseC = lookup.findStatic(Sample.class, "encloseC", type);
        Object result1 = encloseC.invokeWithArguments("<", ">", "囲まれた");
        System.out.println(String.format("result1:%s", result1));
        // 「<<」と「>>」で囲むメソッドを作成
        MethodHandle encloseC2 = MethodHandles.insertArguments(encloseC, 0, "<<", ">>");
        Object result2 = encloseC2.invokeWithArguments("囲まれた");
        System.out.println(String.format("result2:%s", result2));
        // 「<<<」と「>>>」で囲むメソッドを作成
        MethodHandle encloseC3 = encloseC.bindTo("<<<").bindTo(">>>");
        Object result3 = encloseC3.invokeWithArguments("囲まれた");
        System.out.println(String.format("result3:%s", result3));

ぜんぜん実装の説明ができていなくて、、、、

  • コード全体
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class Sample {

    static String sandwich(String enc, String str) {
        return enc + str + enc;
    }

    static String encloseC(String pre, String post, String str){
        return pre + str + post;
    }

    public static void main(String[] args) throws Throwable {
        doSandwich();
        doEncloseC();
    }

    private static void doSandwich() throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        final MethodType type = MethodType.methodType(String.class, String.class, String.class);
        MethodHandle sandwich = lookup.findStatic(Sample.class, "sandwich", type);
        Object result1 = sandwich.invokeWithArguments("*", "sanded!");
        System.out.println(String.format("result1:%s", result1));
        // 「**」で囲むメソッドを作成
        MethodHandle sandwich2 = MethodHandles.insertArguments(sandwich, 0, "**");
        Object result2 = sandwich2.invokeWithArguments("sanded!");
        System.out.println(String.format("result2:%s", result2));
        // 「***」で囲むメソッドを作成
        MethodHandle sandwich3 = sandwich.bindTo("***");
        Object result3 = sandwich3.invokeWithArguments("sanded!");
        System.out.println(String.format("result3:%s", result3));
    }

    private static void doEncloseC() throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType type = MethodType.methodType(String.class, String.class, String.class, String.class);
        MethodHandle encloseC = lookup.findStatic(Sample.class, "encloseC", type);
        Object result1 = encloseC.invokeWithArguments("<", ">", "囲まれた");
        System.out.println(String.format("result1:%s", result1));
        // 「<<」と「>>」で囲むメソッドを作成
        MethodHandle encloseC2 = MethodHandles.insertArguments(encloseC, 0, "<<", ">>");
        Object result2 = encloseC2.invokeWithArguments("囲まれた");
        System.out.println(String.format("result2:%s", result2));
        // 「<<<」と「>>>」で囲むメソッドを作成
        MethodHandle encloseC3 = encloseC.bindTo("<<<").bindTo(">>>");
        Object result3 = encloseC3.invokeWithArguments("囲まれた");
        System.out.println(String.format("result3:%s", result3));
        // ???
        CallSite site = new ConstantCallSite(encloseC3);
        Object result4 = site.dynamicInvoker().invokeWithArguments("囲まれた");
        System.out.println(String.format("result4:%s", result4));
    }
}

コード全体の中で「CallSite」を使ってメソッド呼び出ししていますが、この辺になるともう???です。
id:skrbさんの記事Java技術最前線 - Java SE 7徹底理解 第14回 Javaのためではない機能 - InvokeDynamic:ITproに詳細があります。
昔この記事を見た時は「JRuby」がわからないので、読まずにいままでいました。
Java8の「Lambda」を理解する助けになると思うのでまた読み返そうと思います。
とにかくレベルが高くてよくわかっていないのですが、いろいろ写経するのは楽しいです!
追記、id:bleis-tiftさんにコメントいただいて記載内容を修正しました。部分適用をカリー化と勘違いしていました。
まだまだ勉強不足で、、、