我是一名 JavaScript 程序员,我一直坚持 variadic arguments are harmful。如果我们在 JavaScript 中没有可变参数函数(只是远离 arguments 对象),那么可以在 Hindley Milner 类型系统中类型化的 JavaScript 中的每个函数(减去 API 特定函数,如 DOM 函数)都可以轻松转换为OCaml 中的等效函数。
那么,apply 函数的 OCaml 等价物是什么?我相信这是正常的功能应用:
let apply f x = f x (* equivalent of apply in JavaScript *)
普通函数应用程序如何等效于 JavaScript 中的 apply 函数?考虑:
let s f g x = f x (g x) (* the S combinator from the SKI combinator calculus *)
这个函数将用 JavaScript 编写如下:
var s = function (f) {
return function (g) {
return function (x) {
return f(x)(g(x));
};
};
};
请注意,每个函数定义和函数调用都以柯里化形式显式编写。
这是 JavaScript 和 OCaml 的区别:
- 在 OCaml 中,默认情况下所有函数都是 curry,您必须明确取消它们。
- 在 JavaScript 中,默认情况下所有函数都是非柯里化的,您必须明确地柯里化它们。
那么,让我们看一下 S 组合子的非柯里化变体。一、OCaml:
let s (f, g, x) = f (x, g (x)) (* sml convention is to use uncurried functions *)
JavaScript 中的等价物:
var s = function (f, g, x) {
return f(x, g(x));
};
请注意,OCaml 和 JavaScript 中的普通函数应用程序是相同的。对于咖喱函数:
let result = s f g x (* equivalent to `((s f) g) x` *)
JavaScript 中的等价物:
var result = s(f)(g)(x);
对于非柯里化函数:
let result = s (f, g, x)
JavaScript 中的等价物:
var result = s(f, g, x);
那么apply 函数呢?怎么相当于普通函数应用?
在 OCaml 中,您可以这样做:
let args = (f, g, x) (* args is a tuple *)
let result = s args (* normal function application *)
JavaScript 中的等价物是:
var args = [f, g, x]; // args is an array
var result = s.apply(null, args); // normal function application
正如你所见,OCaml 中的元组等同于 JavaScript 中的数组。 JavaScript 中的数组是通用的。它们可以用作列表或元组,具体取决于上下文。
给apply 的args 参数可以是任何类似数组的对象,它被视为单个元组参数。 JavaScript 中的每个函数都可以被认为是一个单参数函数。 JavaScript 中的多参数函数可以被认为是一个单参数元组参数函数。 JavaScript的apply函数只是普通函数应用的一种特殊形式。
那么这意味着什么?考虑:
var negate = function (f) {
return function () {
return !f.apply(null, arguments);
};
};
如果我们认为arguments 是内部函数的隐式参数,那么上述函数在OCaml 中的等价物是:
let negate f = fun arguments -> not (f arguments) (* arguments is explicit *)
这可以简化为:
let negate f x = not (f x)
现在,您可能会说这仅适用于单参数函数。事实并非如此。 negate 的类型签名是:
val negate : ('a -> bool) -> 'a -> bool
因此,它适用于任何类型'a,包括元组。这相当于 JavaScript 中的多参数函数只是单参数元组参数函数。
最后,唯一真正的问题是将柯里化函数转换为非柯里化函数,以便您可以negate 它们。不幸的是,在 OCaml 中没有通用的方法来取消函数。所以你需要一个函数族来uncurry几个arities的柯里化函数:
let uncurry2 f (x, y) = f x y
let uncurry3 f (x, y, z) = f x y z
.
.
.
.
在否定函数后,你可以curry 他们回来。然而,就像uncurry 一样,没有办法将curry 泛型化为一个函数。因此,您再次需要一系列 curry 函数:
let curry2 f x y = f (x, y)
let curry3 f x y z = f (x, y, z)
.
.
.
.
创建泛型curry 或uncurry 函数的唯一方法是使用动态类型语言(如Lisp 或JavaScript)或依赖类型语言(如Idris 或Agda)。 OCaml 的类型系统(Hindley Milner 类型系统)过于严格,无法实现此类功能。