Hatena::ブログ(Diary)

tomoTakaの日記

2013-05-05

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

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

  • まず実行結果

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

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

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;
    }

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さんにコメントいただいて記載内容を修正しました。部分適用をカリー化と勘違いしていました。
まだまだ勉強不足で、、、

bleis-tiftbleis-tift 2013/05/07 09:17 MethodHandles.Lookupなどなど、追加されていたのですね。

それはそうと、この記事での「カリー化」と呼んでいるものは、カリー化ではなく、部分適用と呼ばれるものです。
カリー化は、複数引数を取る関数(例えばAとBを取ってCを返す関数)を、一つの関数を取って「関数」を返す関数にすること(Aを取って「Bを取ってCを返す関数」を返す関数)です。
カリー化したところで、AとBを渡さないとCを得ることはできません。
それに対して、部分適用はAとBが必要な関数に例えばAを(先に)渡してしまって、Bを取ってCを返す関数にすることです。
この記事での

MethodHandle sandwich2 = MethodHandles.insertArguments(sandwich, 0, "**");

の部分は、引数を2つ取るsandwich関数の第一引数を先に渡すことで、引数を一つとるsandwich2関数を作っていることになるので、カリー化ではなく部分適用です。

みずしまさんの、以下のエントリなども参考にどうぞ。

http://d.hatena.ne.jp/kmizushima/20091216/1260969166
http://d.hatena.ne.jp/kmizushima/20130317/1363515568

tomoTakatomoTaka 2013/05/07 22:05 ご指摘ありがとうございます。
まだカリー化は難しくて理解できていないですが、部分適用がわかってよかったです
本当にコメントありがとうございます!!!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/tomoTaka/20130505/1367761332