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