Both let plus x y = x + y
and let plus = fun x -> fun y -> x + y
将被编译为相同的代码:
camlPlus__plus:
leaq -1(%rax, %rbx), %rax
ret
是的,正是两条汇编指令,没有任何序言和尾声。
OCaml 编译器执行几个优化步骤,实际上是在不同的类别中“思考”。例如,两个函数都用相同的 lambda 代码表示:
(function x y (+ x y))
我认为,根据上面的 lambda,您可能会认为 OCaml 编译器会转换为非柯里化版本。
Update
我还想补充几句关于核心的内容const
功能。假设我们有 const 函数的两个语义等价表示:
let const_xxx c = (); fun _ -> c
let const_yyy c _ = c
在 lambda 形式中,它们将表示为:
(function c (seq 0a (function param c))) ; const_xxx
(function c param c) ; const_yyy
所以,正如你所看到的,const_xxx
确实是以柯里化形式编译的。
但最有趣的问题是,为什么值得用如此晦涩的代码来编写它。也许汇编输出(amd64)中有一些线索:
camlPlus__const_xxx_1008:
subq $8, %rsp
.L101:
movq %rax, %rbx ; save c into %rbx (it was in %rax)
.L102:
subq $32, %r15 ; allocate memory for a closure
movq caml_young_limit(%rip), %rax ; check
cmpq (%rax), %r15 ; that we have memory, if not
jb .L103 ; then free heap and go back
leaq 8(%r15), %rax ; load closure address to %rax
movq $3319, -8(%rax)
movq camlPlus__fun_1027(%rip), %rdi
movq %rdi, (%rax)
movq $3, 8(%rax)
movq %rbx, 16(%rax) ; store parameter c in the closure
addq $8, %rsp
ret ; return the closure
.L103: call caml_call_gc@PLT
.L104: jmp .L102
关于什么const_yyy
?它被简单地编译为:
camlPlus__const_yyy_1010:
ret
只需返回参数即可。因此,假设实际的优化点是const_xxx
闭包的创建是在函数内部编译的,并且应该很快。另一方面,const_yyy
不希望以柯里化方式调用,因此如果您在没有所有所需参数的情况下调用它,那么编译器需要添加在以下位置创建闭包的代码const_yyy
部分应用(即执行const_xxx
每次你打电话const_xxx x
).
总而言之,const
优化创建一个针对部分应用程序进行优化的函数。虽然,这是有代价的。未优化的const
如果使用所有参数调用函数,其性能将优于优化后的函数。 (实际上我的参数甚至放弃了对const_yyy
当我用两个参数应用它时。