Понимание синтаксиса точек Js.Promise.resolve (.) В ReasonML - PullRequest
0 голосов
/ 04 декабря 2018

Я пытаюсь понять документы: https://reasonml.github.io/docs/en/promise

В разделе использования есть:

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

Почему перед 2 стоит точка?Что это значит и что это делает?

1 Ответ

0 голосов
/ 06 декабря 2018

(. ) как используется здесь, в приложении функции, означает, что функция должна вызываться с соглашением о вызове без кэширования.

При использовании в типе функции, таком как тип resolve здесь, (. 'a) => unit, это означает, что функция не выполняется.

Хорошо, так что, черт возьми, это значит?Wweeellll, это немного истории.Здесь идет речь:

Что такое нескрипление?

Некурриция - это противоположность карри, поэтому давайте сначала объясним это, а затем сравним.

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

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

Третий тип функции - resolve иreject, которые являются функциями обратного вызова, переданными из JavaScript и, таким образом, не выполненными.Но это также 1-арные функции, в которых вы думаете, что карри и некуривая форма должны быть абсолютно одинаковыми.И с обычными мономорфными функциями вы были бы правы, но, к сожалению, resolve является полиморфным, что создает некоторые проблемы.

Если бы возвращаемый тип был полиморфным, функция на самом деле могла бы быть не 1-аричной в форме карри, поскольку возвращаемое значение само по себе может быть функцией, принимающей другой аргумент, который может возвращать другую функцию и так далее.Это один из недостатков карри.Но, к счастью, это не так, поэтому мы знаем, что это 1 год.

Я думаю, что проблема еще более тонкая, чем эта.Это может возникнуть из-за того, что мы должны иметь возможность представлять 0-иные неиспользуемые функции, используя типы каррированных функций, которые все являются 1-ариальными.Как мы это делаем?Ну, если бы вы реализовали эквивалентную функцию в Reason / OCaml, вы бы использовали unit в качестве типа аргумента, так что давайте просто сделаем это.Но теперь, если у вас есть полиморфная функция, она может быть 0-ой, если она мономорфизирована как unit, и 1-ой иначе.И я полагаю, что вызов 0-арной функции с одним аргументом в некотором роде был признан несостоятельным.

Но почему же тогда reject нужно не выполнять, если это не полиморфно?

Ну ... я думаю, что это просто для согласованности.


Для получения дополнительной информации см. руководство (но обратите внимание, что оно путает карри с частичным применением)

...