如何使用Js.Option.map?

How to use Js.Option.map?

对于此代码:

  // val map : ('a -> 'b [@bs]) -> 'a option -> 'b option
  let optTest: Js.Option.t(int) = Js.Option.map(x => x, Js.Option.some(1));

我收到以下错误:

  This expression should not be a function, the expected type is (. 'a) => 'b

其中 x => x 为红色。我真的很困惑,为什么 map 不起作用?从它的类型签名看来我使用它是正确的,但编译器说第一个参数不应该是一个函数?

简答 - 使用Belt.Option.map代替:

let optTest: option(int) = Belt.Option.map(Some(1), x => x);

长答案:

Js 命名空间主要用于绑定到 JavaScript 的标准 API。虽然 Js.Option 由于历史原因被包含在此命名空间中,但 Js 命名空间中使用的约定仍然是非常薄的绑定。

您在文档中看到的回调函数类型 'a -> 'b [@bs] 和您在错误消息中看到的类型 (. 'a) => 'b 是完全相同的类型。但前者是 OCaml 的语法,而后者是 Reason 的语法,并且还加糖以减少攻击性。无论哪种方式,问题是当它期望这种奇怪的其他类型的函数时,你传递给它的是一个普通函数。

另一种奇怪的函数称为 uncurried 函数。之所以这样称呼是因为 Reason 中的 "normal" 函数是柯里化的,而 JavaScript 函数不是。因此,未柯里化的函数本质上只是一个原生 JavaScript 函数,您有时需要处理它,因为您可能会收到一个函数或需要将一个函数传递给更高阶的 JavaScript 函数,例如 Js命名空间。

那么如何在 Reason 中创建一个未柯里化的函数呢?只需添加一个 .,如类型:

let optTest: option(int) = Js.Option.map((.x) => x, Some(1);

或者如果你想不加糖(你不加糖,但为了完整起见):

let optTest: option(int) = Js.Option.map([@bs] x => x, Some(1);

附录:

您可能已经注意到,我已将示例中的 Js.Option.tJs.Option.some 替换为 optionSome。那是因为那些是实际的基元。 option 类型本质上定义为

type option('a) =
  | Some('a)
  | None

随处可见。

Js.Option.t('a)(和Belt.Option.t('a))只是一个别名。 Js.Option.some 只是一个方便的函数,在 Belt.Option 中没有任何等价物。它们主要只是为了保持一致性,您通常应该改用实际的类型和变体构造函数。