Haskell OCaml参数传递方案中的断点
今天,我正在浏览的是'sHaskell OCaml参数传递方案中的断点,haskell,ocaml,ocaml-core,Haskell,Ocaml,Ocaml Core,今天,我正在浏览的是'sCore\u kernel模块的源代码,我使用了compose函数: (*这些函数的典型用例是传入函数参数 因此,我们告诉编译器 在参数传递方案中插入断点。*) 设f g=();乐趣x->f(gx) 我会将compose函数定义为: 让组合f g x=f(g x) 他们这样定义compose的原因是“因为compose是一个函数,它将函数f和g作为参数,并返回函数fun x->f(gx)因此,他们定义了compose的方法,即告诉编译器在参数传递方案中的f和g之后但在
Core\u kernel
模块的源代码,我使用了compose
函数:
(*这些函数的典型用例是传入函数参数
因此,我们告诉编译器
在参数传递方案中插入断点。*)
设f g=();乐趣x->f(gx)
我会将compose
函数定义为:
让组合f g x=f(g x)
他们这样定义compose
的原因是“因为compose
是一个函数,它将函数f
和g
作为参数,并返回函数fun x->f(gx)
因此,他们定义了compose
的方法,即告诉编译器在参数传递方案中的f
和g
之后但在x
之前插入断点。”
所以我有两个问题:
compose
会有什么不同来自Haskell,这个约定对我来说没有任何意义。这是一个效率黑客,以避免在注释中指出的预期用例中部分应用程序的成本 OCaml将curried函数编译成固定的arity构造,必要时使用闭包部分应用它们。这意味着该arity的调用是有效的——没有闭包构造,只有一个函数调用 对于
funx->f(gx)
,在compose
中将有一个闭包构造,但这将比部分应用程序更有效。部分应用程序生成的闭包要经过一个包装器caml\u curryN
,该包装器的存在是为了确保效果在正确的时间发生(如果闭包本身部分应用)
编译器选择的固定算术是基于一个简单的语法分析——本质上,一行中有多少个参数,中间没有任何内容。Jane St.程序员已经用它通过注入()
“中间”参数来选择他们想要的算术
简而言之,let compose f g x=f(g x)
是一个不太理想的定义,因为它会导致compose f g
的常见双参数情况成为一个更昂贵的部分应用程序
当然,在语义上没有任何区别。值得注意的是,OCaml中部分应用程序的编译已经得到了改进,这种性能攻击不再是必要的。是否会构建一个闭包来返回
fun x->f(gx)
?与部分应用程序相关的关闭相比,创建此关闭是否更便宜?我看不出一个明显的原因。由部分应用程序构建的闭包涉及一个额外的间接过程-它们通过caml\u curryN
(这对于正确的求值顺序是必要的),而不是直接到代码。编译器不处理let compose f g=fun x->f(gx)吗
和让f g x=f(g x)
以不同的方式组合?我不会想到插入一个()
。不,这些都是一样处理的let f x y=…
只是let f=fun x->fun y->…
的糖:展开时,两者是相同的。明白了。在Haskell中,内联线对它们的处理略有不同。使用()很有意义;不过,在OCaml中也有类似的技巧。当影响内联线时,Haskell中也会发生类似的事情。下面的映射定义比更简单的定义优化得更好:map f=go where{go[]=[];go(a:as)=f a:go as}
@J.Abrahamson这只因为map
是递归的,所以才有用。因此,go as
比map f as
更有效。考虑到compose
不是递归的,我看不出同样的概念如何应用于它。也有非递归的实例。我的观点是,内联线仅在定义中的所有变量都已填充时运行,因此在fx=\y->…
和fxy=…
之间的行为不同,尽管它们在语义上是相同的。