When I was learning OCaml essentials, I was told that every function in OCaml is actually a function with only one parameter. A multi-argument function is actually a functio
Both let plus x y = x + y
and let plus = fun x -> fun y -> x + y
will be compiled to the same code:
camlPlus__plus:
leaq -1(%rax, %rbx), %rax
ret
Yes, exactly two assembler instructions, without any prologues and epilogues.
OCaml compiler performs several steps of optimizations, and actually "thinks" in a different categories. For example, both functions are represented with the same lambda code:
(function x y (+ x y))
I think, that according to the lambda above, you may think that OCaml compiler transforms to a non-curried version.
I would also like to add a few words about the core's const
function. Suppose we have two semantically equivalent representations of the const function:
let const_xxx c = (); fun _ -> c
let const_yyy c _ = c
in a lambda form they will be represented as:
(function c (seq 0a (function param c))) ; const_xxx
(function c param c) ; const_yyy
So, as you can see, const_xxx
is indeed compiled in a curried form.
But the most interesting question, is why it is worth to write it in a such obscure code. Maybe there're some clues in assembly output (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
What about const_yyy
? It is compiled simply as:
camlPlus__const_yyy_1010:
ret
Just return the argument. So, it is assumed that the actual point of optimization, is that in const_xxx
the closure creation is compiled inside the function and should be fast. On the other hand, const_yyy
doesn't expect to be called in a curried way, so if you will call it without all the needed parameters, then compiler needs to add the code that creates a closure in the point of const_yyy
partial application (i.e., to perform all the operations in the const_xxx
every time you call const_xxx x
).
To conclude, const
optimization creates a function that is optimized for partial application. Although, it comes with cost. A non-optimized const
function will outperform the optimized if they are called with all parameters. (Actually my parameter even droped a call to const_yyy
when I applied it with two args.