今天,我正在浏览Jane Street Core_kernel
模块的源代码和came across compose
函数的源代码:
(* The typical use case for these functions is to pass in functional arguments
and get functions as a result. For this reason, we tell the compiler where
to insert breakpoints in the argument-passing scheme. *)
let compose f g = (); fun x -> f (g x)
我会将compose
函数定义为:
let compose f g x = f (g x)
他们给出定义compose
方式的原因是“因为compose
是一个函数,它将函数f
和g
作为参数并返回函数{ {1}}结果,他们定义fun x -> f (g x)
他们的方式告诉编译器在compose
和f
之后但在g
之前插入断点 - 通过计划。“
所以我有两个问题:
x
会有什么不同?来自Haskell,这个惯例对我没有任何意义。
答案 0 :(得分:3)
这是一个效率黑客,以避免在评论中指出的预期用例中部分应用的成本。
OCaml将curried函数编译成fixed-arity结构,使用闭包在必要时部分应用它们。这意味着该arity的调用是有效的 - 没有闭包构造,只是一个函数调用。
将成为compose
fun x -> f (g x)
内的闭包构造,但这比部分应用程序更有效。部分应用程序生成的闭包通过一个包装器caml_curryN
来存在,以确保在正确的时间发生效果(如果该闭包本身已部分应用)。
编译器选择的固定arity基于简单的语法分析 - 实际上,连续多少个参数在两行之间没有任何内容。 Jane St.程序员使用它来选择他们想要的arity,方法是注入()
"参数。
简而言之,let compose f g x = f (g x)
是一个不太理想的定义,因为它会导致compose f g
的常见双参数情况成为更昂贵的部分应用。
在语义上,当然,没有任何区别。
答案 1 :(得分:2)
值得注意的是,部分应用程序的编译在OCaml中得到了改进,并且不再需要这种性能攻击。