3

我正在尝试了解文档: https ://reasonml.github.io/docs/en/promise

在使用部分有:

let myPromise = Js.Promise.make((~resolve, ~reject) => resolve(. 2));

为什么2之前有点?它是什么意思,它有什么作用?

4

1 回答 1

6

(. )如此处所使用的,在函数应用程序中,意味着函数应该使用非柯里化调用约定来调用。

在函数类型中使用时,例如resolve这里的类型(. 'a) => unit,表示该函数是非柯里化的。

好吧,那到底是什么意思?Wweeell,这是一个故事。开始:

什么是不客气的?

Uncurrying 是 currying 的反义词,所以让我们先解释一下,然后再进行比较。

柯里化是将一个接受多个参数的函数转换为一系列函数的过程,每个函数都只接受一个参数并返回最终返回值或接受下一个参数的函数。在 Reason/OCaml 中,这是自动为我们完成的,这就是为什么在 OCaml 中,函数类型在其参数之间有箭头(例如'a -> 'b -> 'ret)。您也可以在 Reason ( ) 中以这种方式编写函数类型'a => 'b => 'ret,但默认情况下它被语法 ( ('a, 'b) => 'ret) 隐藏,这很好,但也可能使人们更难理解为什么函数在某些情况下会出现意外行为。

在支持一流函数的非柯里化语言中,您还可以手动柯里化函数。让我们看一个 ES6 中的示例。这是一个普通的“非咖喱” ES6 函数:

let add = (a, b) => a + b;

这是它的咖喱形式:

let add = a => b => a + b;

并用括号强调单独的功能:

let add = a => (b => a + b);

第一个函数接受参数a,然后返回一个函数(结束a),该函数接受参数b,然后计算最终返回值。

这很酷,因为我们可以在a不使用的情况下轻松地部分应用参数bind,但是一次将所有参数应用到它有点不方便,因为我们必须单独调用每个函数:

let result = add(2)(3);

因此,Reason/OCaml 不仅在创建时自动柯里化函数,而且还提供了一个调用约定,让我们也可以方便地应用多个参数。

这一切都很好!...只要每个函数都被柯里化。但是后来我们想和 JavaScript 一起出去玩,大多数函数都没有(但请参阅 Ramda 了解一个值得注意的例外)。为了能够调用 uncurried JavaScript 函数,我们需要一个 uncurried调用约定,并且为了能够创建可以按预期从 JavaScript 调用的函数,我们需要一个 uncurried函数类型

为什么resolve需要不加咖喱?

一个更好的问题可能是“为什么不是所有的外部函数都是 uncurried”?答案是它们实际上是,但类型和调用约定通常都可以在编译时推断出来。如果不是这样,它通常可以通过检查函数值在运行时“反映”,而性能成本很小(但很快就会复合)。例外情况是它有点混乱,因为文档没有准确解释何时需要显式 uncurrying 才能正确运行,以及何时不需要但出于性能原因可能有益。此外,对于非柯里化函数,实际上有两个注释,一个可以推断调用约定,另一个需要它是显式的,就像这里的情况一样。但这是我收集到的。

让我们看一下 的完整签名Js.Promise.make,这很有趣,因为它包含三种非柯里化函数:

[@bs.new]
external make :
    ([@bs.uncurry] (
        (~resolve: (. 'a) => unit,
         ~reject: (. exn) => unit) => unit)) => t('a) = "Promise";

或者在 OCaml 语法中,我发现在这种情况下更具可读性:

external make : (resolve:('a -> unit [@bs]) ->
                 reject:(exn -> unit [@bs]) -> unit [@bs.uncurry]) -> 'a t = "Promise" [@@bs.new]

第一种函数是make它自己,它是一个外部的,可以推断为非柯里化,因为所有的外部当然都是用 JavaScript 实现的。

第二种函数是我们将创建并传递给make. 这必须是 uncurried 的,因为它是从 JavaScript 调用的,具有 uncurried 调用约定。但是由于我们创建的函数默认[@bs.uncurry]是柯里化的,所以这里使用它来指定它需要一个未柯里化的函数,并且它应该是自动未柯里化的。

第三种函数是resolveand reject,它们是从 JavaScript 传回的回调函数,因此是非柯里化的。但这些也是一元函数,您认为咖喱和非咖喱形式应该完全相同。对于普通的单态函数,你是对的,但不幸resolve的是它是多态的,这会产生一些问题。

如果返回类型是多态的,则函数实际上可能不是柯里化形式的一元,因为返回值本身可能是一个接受另一个参数的函数,它可以返回另一个函数,依此类推。这是柯里化的一个缺点。但幸运的是它不是,所以我们知道它是一元的。

我认为问题比这更微妙。这可能是因为我们需要能够使用柯里化函数类型来表示 0 元非柯里化函数,这些函数类型都是 1 元的。我们如何做到这一点?好吧,如果你要在 Reason/OCaml 中实现一个等效函数,你会使用unit它作为参数类型,所以让我们这样做。但是现在,如果你有一个多态函数,如果它被单态化为 0 元,否则它可能是unit1 元。而且我想用一个参数调用一个 0 元函数在某种程度上被认为是不合理的。

但是,当它不是多态的时候,为什么reject需要非柯里化呢?

嗯......我最好的猜测是这只是为了保持一致性。


有关更多信息,请参阅手册(但请注意,它会将柯里化与部分应用混淆)

于 2018-12-06T03:15:07.873 回答