了解ReasonML中的Js.Promise.resolve(。)点语法

时间:2018-12-04 17:35:44

标签: promise reason bucklescript

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

在用法部分,有:

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

为什么2前面有点?它是什么意思,它是做什么的?

1 个答案:

答案 0 :(得分:4)

(. )在函数应用程序中使用,表示该函数应使用非咖喱的调用约定来调用。

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

好,那到底是什么意思? Wweeellll,这只是一个故事。去吧:

什么是临时的?

非currying与currying相反,所以让我们先解释一下然后再进行比较。

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对话,而大多数功能都不是JavaScript(但请参见Ramda作为一个值得注意的例外)。为了能够调用未咖喱的JavaScript函数,我们需要一个未咖喱的调用约定,并且要能够创建可以从JavaScript调用的函数,我们需要一个未咖喱的 function type

为什么resolve不需要处理?

一个更好的问题可能是“为什么不是所有外部函数都是不速写的”?答案是它们实际上是,但是类型和调用约定通常可以在编译时进行推断。如果不是这样,通常可以在运行时通过检查函数值来“反映”它,而这会以很小的(但很快会增加)性能成本。例外情况是它有些混乱,因为文档没有确切说明何时需要显式的连续显示才能正常运行,什么时候不需要,但出于性能原因可能会有所帮助。此外,实际上有两个针对非咖喱函式的注解,一个可以推论调用约定,另一个要求它必须是显式的,在这里就是这种情况。但这就是我收集的。

让我们看一下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的回调。这必须是非咖喱的,因为它是使用非咖喱的调用约定从JavaScript调用的。但是由于默认情况下我们创建的函数是咖喱的,因此[@bs.uncurry]在此处用于指定它希望使用的是非咖喱的功能,并且应该自动将其取消咖喱。

第三种函数是resolvereject,它们是从JavaScript传回的回调函数,因此不被使用。但是,这些也是一元函数,您会认为curried和uncurried形式应该完全相同。使用普通的单态函数是正确的,但是不幸的是resolve是多态的,这会带来一些问题。

如果返回类型是多态的,则该函数实际上可能不是咖喱形式的1-ary,因为返回值本身可以是带有另一个参数的函数,可以返回另一个函数,依此类推。那是欺骗的缺点。但幸运的是它不是,所以我们知道它是一元。

我认为这个问题比这更棘手。之所以会出现这种情况,是因为我们需要能够使用全部为1进制的咖喱函数类型来表示0进制的非咖喱函数。我们该怎么做?好吧,如果您要在Reason / OCaml中实现等效功能,则可以使用unit作为参数类型,所以我们就可以做到这一点。但是现在,如果您具有多态函数,则将其单态化为unit时,它可能是0元,否则是1元。我想用一个参数调用0元函数在某种程度上被认为是不合理的。

但是,为什么reject不是多态的,则需要不进行处理?

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


有关更多信息,请参见the manual(但请注意,它将部分应用程序与curry混淆)