関数のカリー化
CILでカリー化をする時のメモ
部分適用かな?まぁどっちでもいいや
クロージャをCILで実装するのに部分適用でやりたかったらメモ
例えばこんな感じの3引数の関数で先頭の引数をカリー化しちゃう
curryなんて関数があった場合
int32 f1(int32 a, int32 b, int32 c) { return a + b + c; } bind = curry(f1, 1) Console.WriteLine(bind(2, 3)) // => 6
引数の数に応じてカリー化を行うクラスを作っておく
関数と1個めの引数は呼び出し側でセットしてもらう
class Curry<T1, T2, T3, R> { public native int f; public T1 var; int32 lambda(T2 x2, T3 x3) { calli self.f(self.var, x2, x3) } }
呼び出し側はCurryのフィールドにセットしつつ
Funcに引き渡してあとはよしなにしてもらう
CILへの変換時に「bind = curry(f1, 1)」をこんな感じのコードにして
bindって引数にこれFuncでっせというフラグを付けておく
そのフラグ見てcallvirtする感じ
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly Global { .hash algorithm 0x00008004 .ver 0:0:0:0 } .method public static int32 f1(int32 a, int32 b, int32 c) { // return a + b + c ldarg.0 ldarg.1 add ldarg.2 add ret } .class public Curry<T1, T2, T3, R> { .field public native int f .field public !T1 var .method instance void .ctor() { ret } .method instance !R lambda(!T2 x2, !T3 x3) { // self.f(self.var, x2, x3) ldarg.0 ldfld !T1 class Curry<!T1, !T2, !T3, !R>::var ldarg.1 ldarg.2 ldarg.0 ldfld native int class Curry<!T1, !T2, !T3, !R>::f calli !R (!T1, !T2, !T3) ret } } .method public static void __EntryPoint() cil managed { .entrypoint .locals ( class Curry<int32, int32, int32, int32> curry, class [mscorlib]System.Func`3<int32, int32, int32> bind ) // Console.WriteLine(f1(1, 2, 3)) ldc.i4.1 ldc.i4.2 ldc.i4.3 call int32 f1(int32, int32, int32) call void [mscorlib]System.Console::WriteLine(int32) // bind = curry(f1, 1) // Console.WriteLine(bind(2, 3)) // curry = new Curry<int32, int32, int32, int32> newobj instance void class Curry<int32, int32, int32, int32>::.ctor() stloc.0 // curry.f = f1 ldloc.0 ldftn int32 f1(int32, int32, int32) stfld native int class Curry<int32, int32, int32, int32>::f // curry.var = 1 ldloc.0 ldc.i4.1 stfld !0 class Curry<int32, int32, int32, int32>::var // bind = new Func<int32, int32, int32>(curry, curry.lambda) ldloc.0 ldftn instance !3 class Curry<int32, int32, int32, int32>::lambda(!1, !2) newobj instance void class [mscorlib]System.Func`3<int32, int32, int32>::.ctor(object, native int) stloc.1 // Console.WriteLine(bind(2, 3)) ldloc.1 ldc.i4.2 ldc.i4.3 callvirt instance !2 class [mscorlib]System.Func`3<int32, int32, int32>::Invoke(!0, !1) call void [mscorlib]System.Console::WriteLine(int32) ldstr "end..." call void [mscorlib]System.Console::WriteLine(string) ret }