Как использовать 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 не работает? Судя по сигнатуре типа, я использую его правильно, но компилятор говорит, что первый аргумент не должен быть функцией?


person menfon    schedule 01.02.2020    source источник


Ответы (1)


Короткий ответ – используйте Belt.Option.map. вместо:

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

Длинный ответ:

Пространство имен Js в основном предназначено для привязки к стандартным API JavaScript. И хотя Js.Option по историческим причинам было включено в это пространство имен, соглашения, используемые в пространстве имен Js, по-прежнему относятся к очень тонким привязкам.

Тип, который вы видите для функции обратного вызова в документации, 'a -> 'b [@bs], и тип, который вы видите в сообщении об ошибке, (. 'a) => 'b, полностью совпадают. Но первое используется в синтаксисе OCaml, а второе — в Reason, а также подслащено, чтобы выглядеть менее оскорбительным. В любом случае, проблема в том, что вы передаете ей обычную функцию, когда она ожидает какую-то странную другую функцию.

Странная другая функция называется uncurried. Это называется так потому, что «обычные» функции в Reason каррируются, а функции 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.t и Js.Option.some на option и Some. Это потому, что это настоящие примитивы. Тип option по существу определяется как

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

и доступен везде.

Js.Option.t('a)Belt.Option.t('a)) — это просто псевдоним. А Js.Option.some — это просто вспомогательная функция, которая не имеет эквивалента в Belt.Option. В основном они предназначены только для согласованности, и вместо этого обычно следует использовать фактические конструкторы типов и вариантов.

person glennsl    schedule 01.02.2020
comment
Спасибо за такой полный ответ. Я только изучаю причину, и чтение вашего объяснения действительно помогло мне лучше понять ее. Здорово, когда кто-то прилагает усилия, чтобы помочь сообществу. - person Dmitry Biletskyy; 02.02.2020