为什么 F# 不将柯里化编译成单独的函数?

Why doesn't F# Compile Currying into Separate Functions?

所以我正在尝试学习 F#,并且在学习新事物时我喜欢查看 IL 以了解幕后发生的事情。我最近读到有关 Currying 的内容,这是该语言的一个明显基础。

根据 F# for fun and Profit 创建以下函数时:

let addItems x y = x + y

真正发生的是创建了两个单参数函数。

let addItems x =
    let subFunction y = 
          x + y
    subFunction

并且当您使用 addItems 5 6 调用函数时,操作顺序如下

  1. addItems 使用参数 5

  2. 调用
  3. addItems returns subFunction

  4. 使用参数 6 调用子函数
  5. subFunction 在范围内有参数 5,所以它添加 returns 5 和 6 的总和

所有这一切表面上听起来都很好。但是,当您为此查看 IL 时,它讲述了一个不同的故事。

.method public static int32  testCurry(int32 x,
                                       int32 y) cil managed
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) 
  // Code size       5 (0x5)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  ret
} // end of method Sandbox::testCurry

我们可以在 IL 中清楚地看到,创建了一个带有两个参数和 returns Int32 的静态函数。

所以我的问题是,为什么会出现差异?这不是我第一次看到与文档不一致的 IL...

So my question is, why the discrepancy?

就行为契约而言,实际编译的 IL 不需要,实际上也不应该重要。通过编译为单个函数,调用在 JIT/runtime 级别得到明显更好的优化。

"what is really happening here..." 不一定是实际发生的情况,更多的是 "how this should be reasoned about when writing and using F# code is..."。底层实现应该可以根据需要自由更改,以充分利用运行时环境。