почему обертывание функций в iife вызывает слабые типы? - PullRequest
3 голосов
/ 19 октября 2019

Я пытаюсь найти способ скрыть определенные вспомогательные функции и связанные с ними вещи от пользователя модуля, и подумал, что использование IIFE будет работать, но не получится, потому что переменная типа не может быть обобщена?

Я думаю, что я свел это к самому базовому сценарию со следующим кодом:

module TestA = {
  let y = 0;
  let x = (type a, numbers: list(a)): option(a) => None;
};

module TestB = {
  let x =
    (
      () => {
        let y = 0;
        (type a, numbers: list(a)): option(a) => None;
      }
    )();
};

В TestB компилятор жалуется на

  41 │ };
  42 │ 
  43 │ module TestB = {
  44 │   let x =
   . │ ...
  50 │     )();
  51 │ };
  52 │ 
  53 │ module Number = {

  The type of this module contains type variables that cannot be generalized:
  { let x: list('_a) => option('_a); }

  This happens when the type system senses there's a mutation/side-effect,
  in combination with a polymorphic value.
  Using or annotating that value usually solves it. More info:
  https://realworldocaml.org/v1/en/html/imperative-programming-1.html#side-effects-and-weak-polymorphism

почему это так? И как я мог бы подойти к проблеме сокрытия y от пользователя модуля?

Ps: При переформатировании аннотации возвращаемого типа в TestB ставится после None, например, так: (type a, numbers: list(a)) => (None: option(a)),Почему здесь, а не в модуле TestA? Насколько я понял, это просто «помечает» возвращаемое значение, поэтому я не вижу здесь разницы?

1 Ответ

3 голосов
/ 19 октября 2019

Вы нажали так называемое ограничение значения . Короче говоря, компилятор считает, что могут быть потенциальные побочные эффекты, происходящие внутри замыкания, которые могут небезопасно изменить тип возвращаемого значения функции. Поэтому он не может определиться с типом возврата.

К счастью, ReasonML имеет простую идиоматическую замену для IIFE - ограниченных скобками областей :

module TestA = {
  let x = {
    let y = 0;
    numbers => None;
  };
};

Эта область действия скрывает y внутри определения x.

Более общий способ скрыть элементы в модуле - дать модулю подпись, которая просто не перечисляет скрытые элементы. В этом случае это будет выглядеть так:

module TestA: {
  let x: list('a) => option('a);
} = {
  let y = 0;
  let x = numbers => None;
};

Другим способом будет использование «файла интерфейса», например:

// TestA.rei
let x: list('a) => option('a);

// TestA.re
let y = 0;
let x = numbers => None;
...