【问题标题】:OCaml equivalent of javascript 'apply'OCaml 等效于 javascript 'apply'
【发布时间】:2015-06-25 01:56:49
【问题描述】:

我已经有一段时间没有编写 OCaml 了,我遇到了这个听起来很简单的问题,但我在解决问题时遇到了障碍:

编写一个函数,该函数接收带有可变数量参数的函数f,该函数返回一个布尔值(即f 的类型为'a -> 'b -> 'c -> ... -> bool)并返回一个函数g,它表示@987654327 的否定@(即所有有效参数集的 (f x1 x2 .. xn) == not (g x1 x2 .. xn))。

它的灵感来自以下解决 Javascript 问题的代码块:

function negate(func) {
  return function() {
    return !func.apply(null, arguments);
  };
}

(来自http://eloquentjavascript.net/1st_edition/chapter6.html

但是,由于函数f 没有预设数量的参数,我看不到在 OCaml 中实现此功能的方法(“参数”关键字或等效关键字不可用)。我找到了有关处理具有可变数量参数的函数的链接(例如https://blogs.janestreet.com/variable-argument-functions/),但我想知道是否有更简单/更“自然”的方法来处理这个特定问题。

【问题讨论】:

  • 基本问题是 JavaScript 没有类型系统,而 OCaml 有。从不自然的需求开始,很难得出“自然”的 OCaml 代码。对于它的价值,我不觉得这种 JavaScript 代码是“雄辩的”。一个更好的术语是“噩梦”。只是我个人的看法。
  • “JavaScript 没有类型系统”? alert(1==="1") 另有说法,但是为什么参数的类型对arity 路由器很重要呢? OP:您可以为 0-5 个参数制作一个“arity 金字塔”和硬编码处理程序,这对于大多数程序来说已经足够了。没有那么酷,但最终可能会更快地执行。 js 版本最好使用 bind() 和/或 Function.prototype 使 negate() 成为函数方法,而不是每次自定义都使用匿名包装器。

标签: javascript functional-programming ocaml variadic-functions


【解决方案1】:

我是一名 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 的区别:

  1. 在 OCaml 中,默认情况下所有函数都是 curry,您必须明确取消它们。
  2. 在 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 中的数组是通用的。它们可以用作列表或元组,具体取决于上下文。

applyargs 参数可以是任何类似数组的对象,它被视为单个元组参数。 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)

         .
         .
         .
         .

创建泛型curryuncurry 函数的唯一方法是使用动态类型语言(如Lisp 或JavaScript)或依赖类型语言(如Idris 或Agda)。 OCaml 的类型系统(Hindley Milner 类型系统)过于严格,无法实现此类功能。

【讨论】:

  • 你不应该做.apply(this, arguments) 来真正传递函数的所有参数吗? :-)
  • @Bergi 我不知道如何将其转换为 OCaml,但是是的,理想情况下您应该这样做。 =P
  • 我猜它类似于apply f (self, x) - 接收者的元组和实际的“参数”。所以当我们对我们的 JS 函数进行 curry 时,我们可以给它们一个额外的 self 参数,就像 Python 方法有它一样。
【解决方案2】:

在 ocaml 中接受多个参数的函数实际上是一个接受一个参数并返回另一个函数的函数。 这是咖喱。

你想用非柯里化函数来实现,即只接受一个参数的函数(可能是一个元组):

例如,您需要f : (a * b * c) -> bool 而不是f : a -> b -> c -> bool。但是你必须“手动”转换你的函数。

您可以对 let uncurry f (x,y) = f x y 之类的函数进行正确处理,但这只是在传输问题,因为您必须对任意数量的参数执行此操作。

也许您可以否定将参数列表作为参数的函数。 我的意思是我不知道你想要做什么的细节。

【讨论】:

  • 不尝试做任何特别的事情。只是好奇是否可以在不修改函数本身的情况下完成某些事情。我想不会吧?
  • 没有。以您描述的一般方式是不可能的。参数的类型可以是多态的,参数的数量不能(这只是 Theo 描述的一个小不便)。
【解决方案3】:

这并不难,但您必须手动为每个 arity 编写(并在调用站点选择)一个明确的定义,因为 OCaml 缺乏必要的机制来抽象不同 arity 的相似定义。

请注意,这是 OCaml 代码中已经存在的模式:请参阅 List.map(2)List.iter(2) 等。

定义可能如下所示:

let negate f = (fun a -> not (f a))
let negate2 f = (fun a b -> not (f a b))
let negate3 f = (fun a b c -> not (f a b c))
(* etc *)

请注意,允许这种多态性的类型系统是可以想象的:实际上 Typed Racket 可能能够表达这种定义。

【讨论】:

  • 我的问题是关于一般构造否定函数 - 因为传递给它的函数可以有任意数量的参数,所以这个解决方案不起作用。
猜你喜欢
  • 1970-01-01
  • 2012-01-21
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 2012-08-19
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多