Vladimir
Vladimir

Reputation: 1660

Why F# currying "flattens" function type?

The following function:

let twoInputs x y =
    let sum = x + y
    let product a = sum * a
    product

Has the type:

val twoInputs : x:int -> y:int -> (int -> int)

That's perfectly reasonable, I see why it's coming. But why this function:

let oneInput = twoInputs 1

is of type val oneInput : (int -> int -> int) ?

Shouldn't it be int -> (int -> int) ?

Also, I think the functions above should comply with Associative property, so there should be no differences between int -> int -> (int -> int) and int -> int -> int -> int. If so, why not just specify the latter as the function type for twoInputs?

Upvotes: 2

Views: 144

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

Parentheses mean "value of type FsharpFunc<_>", while the absence of parentheses means "true CLR method". In your example, twoInput is compiled to a true CLR method, but returns a value of type FSharpFunc<_>, hence its type. But your oneInput is compiled to a class field of type FSharpFunc<_>, and hence its type.

You can actually achieve same effect (i.e. turn true method into value) even without currying, very simply:

> let twoInputs x y =
>     let sum = x + y
>     let product a = sum * a
>     product

> let twoInputsAgain = twoInputs

val twoInputs : x:int -> y:int -> (int -> int)
val twoInputsAgain : (int -> int -> int -> int)

This happens because CLR doesn't support the notion of "assigning a method", so F# has to compile this by declaring twoInputsAgain as a field of type FSharpFunc<_> and then assigning to it an instance of a class that inherits from FSharpFunc<_> and calls twoInputs when Invoked.

If you decompile it to C# (I use ILSpy), this is what you see:

static $Program()
{
    $Program.twoInputsAgain@11 = new Program.twoInputsAgain@11();
}

internal class twoInputsAgain@11 : OptimizedClosures.FSharpFunc<int, int, FSharpFunc<int, int>>
{
    public override FSharpFunc<int, int> Invoke(int x, int y)
    {
        return Program.twoInputs(x, y);
    }
}

In conclusion, I want to note that this disctinction doesn't matter very much in practice, unless you practice some really dark magic, so you shouldn't worry about it.

Upvotes: 7

Related Questions