Unlike Scala, for example, F# does not use an alternate syntax to differentiate a “curry-able” function from a “non-curry-able” function. I was therefore interested in what the F# compiler does to when supplied with a function like the following
let add a b c = a + b + c
Does it really generate something like the following code?
let add a = let add1 b = let add2 c = a + b + c add2 add1
Obviously this wouldn’t be great as the code requires closures and sub-functions to be created and so would be less performant in situations where we do not require the currying capabilities.
Thankfully the F# compiler takes care of everything for us so that if we have code in our application which solely uses the add function, like this
let r = add 1 2 3
then the compiler simply generates the following, this is taken from ILSpy from the compiled code and obviously reproduced in C# which I think demonstrates pretty clearly what happens.
public static int add(int a, int b, int c) { return a + b + c; } // therefore our call to the // function becomes int r = Program.add(1, 2, 3);
Now, what happens if we start currying our functions, with code like this
let r = add 1 2 let r1 = r 3
Here the compiler still creates the add method as before (as one would expect) but it also creates the following internal class (I’ve removed attributes to make it all a little cleaner)
internal sealed class r@9 { public int a; public int b; internal r@9(int a, int b) : this() { this.a = a; this.b = b; } public override int Invoke(int c) { return Program.add(this.a, this.b, c); } } // now our calling // code looks like this int a = 1; int b = 2; FSharpFunc r2 = new r@9(a, b); int r = (int)r2.Invoke((!0)3);
So in conclusion, if we’re not using currying on our functions then we get a “standard” function and once we start currying only then do we get the equivalent of sub-functions being created.