F# では関数を引数として取る関数や、関数を戻り値として返す関数を作ることができる。.NET Framework 上でどのようなアセンブリになっているのか関心があったので調べて見た。
まず、次のようなコードを定義する。
let mul x y = x * y;;
let calc f =(f 2 3);;
let addx x = add x;;
この場合、関数 add は 2 つの引数を取って足した結果を返す関数です。同様に関数 mul は掛け算した結果を返す関数です。さてでは、関数 calc と関数 addx は何なのか?
この場合、関数 calc は引数を 2 つ取る関数 f を引数として取り、関数 f に 2 と 3 という引数を与えて答えを返す関数です。つまり、先程の話では引数として関数を取る関数です。関数 addx はもっと、面白い挙動で、この場合引数を 1 つとって、関数 add の残りの引数を引数に持つ関数を返す関数です。計算機科学ではカリー化とも言います。
さて、F# でこれらができるのがわかったところで、コンパイルした結果のアセンブリがどのようになっているかを見てみます。まず、calc からです。
public static T calc(FastFunc<int , FastFunc<int ,T>> f) { return FastFunc<int int ,>.InvokeFast2<t>(f, 2, 3); }
次に、addx です。
[Serializable]
internal class addx@4 : FastFunc<int, int>
{
// Fields
public int x@1;
// Methods
public addx@4(int x@1)
{
this.x@1 = x@1;
}
public override int Invoke(int y@1)
{
return Highorder.add(this.x@1, y@1);
}
}
public static FastFunc<int, int> addx(int x)
{
return new addx@4(x);
}
上記の結果は、F# 1.9.3.14 のコンパイルの結果を .NET Reflector で C# で逆コンパイルしています。見ての通り、calc はある程度想定したとおりですが。addx は 一つのパラメータでインスタンス化した Invoke というメソッドを持つ関数を返す構造になっています。
関数の参照は delegate という形で簡単に表現できますが、関数を返すにはオブジェクトなどの形になっていないと面倒なことになるのでこういう形になったのでしょう。理屈の上では .NET Framework 3.5 以上ではラムダ式とかも使えるはずですが。関数が完全にファーストクラスオブジェクトというわけではないですからね。ラムダ式自体にも制約がありますし、なんでもできるわけではないですから。
![]() |
Foundations of F# (Expert's Voice in .Net)
|


