当我学习OCaml essentials时,我被告知OCaml中的每个函数实际上只是一个只有一个参数的函数.多参数函数实际上是一个函数,它接受一个参数并返回一个函数,该函数接受下一个参数并返回....
这是好事,我明白了.
所以我的问题是:
情况1
如果我做
let plus x y = x + y
Run Code Online (Sandbox Code Playgroud)
在OCaml内部编译时,OCaml会将其更改为let plus = fun x -> fun y -> x + y?
或者相反
案例2
如果我做
let plus = fun x -> fun y -> x + y
Run Code Online (Sandbox Code Playgroud)
OCaml会将其转换为let plus x y = x + y?
哪种情况属实?在正确的情况下,OCaml编译器的优点或优化是什么?
另外,如果情况2为真,那么OCaml正在考虑做什么呢?我的意思是它实际上是相反的,对吗?
这个问题实际上与理解Core的`Fn.const`有关
双方let plus x y = x + y并let plus = fun x -> fun y -> x + y会被编译成相同的代码:
camlPlus__plus:
leaq -1(%rax, %rbx), %rax
ret
Run Code Online (Sandbox Code Playgroud)
是的,正好是两个汇编指令,没有任何序言和结尾.
OCaml编译器执行几个优化步骤,并实际上在不同的类别中"思考".例如,两个函数都使用相同的lambda代码表示:
(function x y (+ x y))
Run Code Online (Sandbox Code Playgroud)
我认为,根据上面的lambda,你可能会认为OCaml编译器转换为非curried版本.
我还想补充一下核心const功能.假设我们有const函数的两个语义等价的表示:
let const_xxx c = (); fun _ -> c
let const_yyy c _ = c
Run Code Online (Sandbox Code Playgroud)
在lambda形式中,它们将表示为:
(function c (seq 0a (function param c))) ; const_xxx
(function c param c) ; const_yyy
Run Code Online (Sandbox Code Playgroud)
因此,正如您所看到的,const_xxx确实是以curry形式编译的.
但最有趣的问题是,为什么值得用一个如此模糊的代码来编写它.也许在汇编输出中有一些线索(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
Run Code Online (Sandbox Code Playgroud)
怎么样const_yyy?它编译简单如下:
camlPlus__const_yyy_1010:
ret
Run Code Online (Sandbox Code Playgroud)
只需返回参数.因此,假设实际的优化点,就是在const_xxx闭包创建中编译函数内部并且应该很快.另一方面,const_yyy不期望以curried方式调用,因此如果你将调用它而没有所有需要的参数,那么编译器需要添加在const_yyy部分应用程序中创建闭包的代码(即,const_xxx每次调用时执行所有操作const_xxx x).
总而言之,const优化会创建一个针对部分应用程序进行优化的函数.虽然,它带来了成本.const如果使用所有参数调用非优化函数,则优化函数将优于优化函数.(实际上我的参数甚至const_yyy在我用两个args应用时调用了一个调用.
就OCaml语言的语义而言,这两个定义完全等同于curry函数的定义.在OCaml语言的语义中没有多参数函数这样的东西.
然而,实施是另一回事.具体而言,OCaml语言的当前实现在其内部表示中支持多参数函数.当curried函数以某种方式定义(即as let f x y = ...或let f = fn x -> fn y -> ...)时,将在内部编译为多参数函数.但是,如果它的定义不同(如let f x = (); fn y -> ...在链接的问题中),它将被编译为curried函数.这只是一种优化,不会以任何方式影响语言的语义.定义curried函数的所有三种方法在语义上都是等价的.
关于什么变成什么的具体问题:由于转换不是从一个OCaml代码转换为另一个OCaml代码,而是从OCaml代码转换为内部表示,我认为最准确的方式来描述它可以说,OCaml的编译器开启双方let plus x y = x + y并let plus = fn x -> fn y -> x + y进入内部同样的事情,而不是它变成一个到另一个.
| 归档时间: |
|
| 查看次数: |
182 次 |
| 最近记录: |