为什么F#currying"扁平化"功能类型?

Vla*_*mir 2 f#

以下功能:

let twoInputs x y =
    let sum = x + y
    let product a = sum * a
    product
Run Code Online (Sandbox Code Playgroud)

有类型:

val twoInputs : x:int -> y:int -> (int -> int)
Run Code Online (Sandbox Code Playgroud)

这是完全合理的,我明白为什么会到来.但为什么这个功能:

let oneInput = twoInputs 1
Run Code Online (Sandbox Code Playgroud)

是类型val oneInput : (int -> int -> int)

不应该int -> (int -> int)吗?

另外,我认为上面的函数应该符合关联属性,所以int -> int -> (int -> int)和之间应该没有区别int -> int -> int -> int.如果是这样,为什么不将后者指定为函数类型twoInputs

Fyo*_*kin 7

括号表示"FsharpFunc <_>类型的值",而没有括号表示"真正的CLR方法".在您的示例中,twoInput编译为真正的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)
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为CLR不支持"分配方法"的概念,因此F#必须通过声明twoInputsAgain为类型字段来编译它FSharpFunc<_>,然后为其分配一个继承的类的实例,FSharpFunc<_>twoInputsInvoked 时调用.

如果您将其反编译为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

总而言之,我想要指出的是,这种区分在实践中并不重要,除非你练习一些非常黑暗的魔法,所以你不应该担心它.