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

関数のカリー化

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
}