Почему F# currying "выравнивает" тип функции?


Следующая функция:

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

Имеет вид:

val twoInputs : x:int -> y:int -> (int -> int)
Это вполне разумно, я понимаю, почему это происходит. Но почему эта функция:
let oneInput = twoInputs 1

Относится к типу val oneInput : (int -> int -> int)?

Разве это не должно быть int -> (int -> int)?

Кроме того, я думаю, что функции выше должны соответствовать ассоциативному свойству, поэтому не должно быть никаких различий между int -> int -> (int -> int) и int -> int -> int -> int. Если да, то почему бы просто не указать последний тип функции для twoInputs?
1 2
f#

1 ответ:

Скобки означают " значение типа FsharpFunc<_>", а отсутствие скобок означает"метод true CLR". В вашем примере twoInput компилируется в метод true CLR, но возвращает значение типа FSharpFunc<_>, следовательно, его тип. Но ваш oneInput компилируется в поле класса типа FSharpFunc<_>, и, следовательно, его тип.

На самом деле вы можете достичь того же эффекта (т. е. превратить истинный метод в ценность) даже без карринга, очень просто:
> 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)

Это происходит потому, что CLR не поддерживает понятие "назначение метода", поэтому F# должен скомпилировать это, объявив twoInputsAgain как поле типа FSharpFunc<_> и затем назначив ему экземпляр класса, который наследует от FSharpFunc<_> и вызывает twoInputs, Когда Invoked.

Если вы декомпилируете его на C# (я использую ILSpy), это то, что вы видите:

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);
    }
}
В заключение я хочу отметить, что это разделение не имеет большого значения на практике, если только вы не практикуете действительно темную магию, поэтому вам не следует беспокоиться об этом.