Как я могу выполнить функцию с необязательными параметрами, которая генерирует объекты Js.t в ReasionML/BuckleScript?

У меня есть следующая функция

[@bs.obj]
external route:
  (
    ~_method: string,
    ~path: string,
    ~action: list(string) => unit,
    ~options: Js.t({..})=?,
    unit
  ) =>
  _ =
  "";

Поскольку функции могут быть частично применены, я ожидаю, что смогу сделать это:

let get = route(~_method="GET");

но это дает мне эту ошибку:

This expression's type contains type variables that can't be generalized:                                                
(~path: string, ~action: list(string) => unit, ~options: {_.. }=?,                                                       
unit) =>
{. "_method": string, "action": list(string) => unit,
  "options": Js.undefined({.. }), "path": string}

Что я здесь делаю неправильно?


person fhdhsni    schedule 22.03.2019    source источник
comment
Возможный дубликат Почему OCaml иногда требует расширения eta?   -  person glennsl    schedule 22.03.2019
comment
Чтобы добавить к ответу, указанному выше, открытые типы объектов, независимо от того, заключены ли они в Js.t или нет, содержат неявную переменную типа, поэтому Js.t({..}) находится в более явной форме Js.t({..} as 'a). И я предполагаю, что компилятор не отличает переменные типа, связанные с объектами, от любой другой переменной типа, так что с его точки зрения переменная типа вполне может быть заменена на ref('a), что потенциально может вызвать проблемы.   -  person glennsl    schedule 22.03.2019
comment
Вполне вероятно, что кто-то с глубоким знанием внутренностей компилятора придет и поправит меня, поэтому я добавил тег ocaml, чтобы привлечь их внимание :)   -  person glennsl    schedule 22.03.2019


Ответы (1)


На самом деле речь идет не о необязательных параметрах и каррировании, а об ограничении значений и необобщенных, т. е. слабых, переменных типа. TL;ДР; либо превратите get в синтаксическую функцию, добавив параметр, например, let get () = route(~_method="GET") ();, либо создайте файл интерфейса *.rei для своего модуля.

Более длинная история

.. переменная строки обозначает полиморфный тип, который компилятор не может свести к обычному мономорфному типу (поскольку очевидно, что эта функция не используется) и не может быть уверен, что частичное приложение route(~_method="GET") на самом деле еще не обращалось к параметру options и может храниться где-то в нем, который должен определять тип.

Следовательно, компилятор не может оставить ее как полиморфную переменную и не может дать конкретный тип, в результате он создает переменную слабого типа, которую можно рассматривать как ссылочную ячейку для будущего определенного конкретного типа. Как неинициализированный тип. Он будет инициализирован позже кодом, использующим функцию get. Если, в конце концов, тип никогда не используется, он может выйти за рамки модуля, что запрещено правилами типизации OCaml/Reason. Следовательно, вы должны либо присвоить ему монотип вручную (т. е. ограничить его каким-либо мономорфным типом), либо создать файл интерфейса, в котором это значение скрыто (т. е. отсутствует) и, следовательно, не может привести к утечке области видимости модуля. По сути, простое создание пустого файла .mli/.rei с тем же именем, что и у вашего файла .ml/.re, решит эту проблему. Другим распространенным решением является превращение get в синтаксическую функцию, то есть что-то с синтаксически явными переменными, например,

let get () = route(~_method="GET") ();

Дальнейшее чтение

person ivg    schedule 22.03.2019