Функция высшего порядка типа reasonml

учитывая следующий модуль, компилятор выдает ошибку

  41 │ };
  42 │ 
  43 │ module TestB = {
  44 │   let minFn = (a, b) => a < b ? a : b;
   . │ ...
  54 │   let max = reduceList(maxFn);
  55 │ };
  56 │ 
  57 │ // module Number = {

  The type of this module contains type variables that cannot be generalized:
  {
    let minFn: ('a, 'a) => 'a;
    let maxFn: ('a, 'a) => 'a;
    let reduceList: ('a, list('b)) => option('b);
    let min: list('_a) => option('_a);
    let max: list('_a) => option('_a);
  }

Похоже, это происходит потому, что я лишь частично применяю аргументы к reduceList. Теперь мне предоставили некоторую информацию об ограничении значений, и мне кажется, что это именно то, что здесь происходит.

Я уже пробовал явно вводить функции min и max там, где они определены, и явно вводить модуль в целом, потому что я подумал, как вы должны обойти это в соответствии с этот раздел об ограничении значений. Однако, похоже, это не имеет значения.

module TestB = {
  let minFn = (a, b) => a < b ? a : b;
  let maxFn = (a, b) => a > b ? a : b;
  let reduceList = (comp, numbers) =>
    switch (numbers) {
    | [] => None
    | [head] => Some(head)
    | [head, ...tail] => Some(List.fold_left(minFn, head, tail))
    };

  let min = reduceList(minFn);
  let max = reduceList(maxFn);
};

С другой стороны ... означает ли здесь начальный _ для типов что-то особенное?


person hesxenon    schedule 19.10.2019    source источник


Ответы (1)


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

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

module TestB = {
  let minFn = (a, b) => a < b ? a : b;
  let maxFn = (a, b) => a > b ? a : b;
  let reduceList = (comp, numbers) =>
    switch (numbers) {
    | [] => None
    | [head] => Some(head)
    | [head, ...tail] => Some(List.fold_left(minFn, head, tail))
    };

  let min = x => reduceList(minFn, x);
  let max : list(int) => option(int) = reduceList(maxFn);
};

Подчеркивание _ в '_a просто означает, что это переменная слабого типа, как объясняется в документации, на которую вы ссылаетесь:

Переменные типа, имя которых начинается с префикса _weak, такого как '_weak1', являются переменными слабо полиморфного типа, иногда их сокращают до переменных слабого типа. Переменная слабого типа - это переменная одного типа, который в настоящее время неизвестен. Как только станет известен конкретный тип t за типом заполнителя _weak1, все вхождения _weak1 будут заменены на t.

person glennsl    schedule 19.10.2019
comment
Вы правы, что в OCaml невозможно удалить переменную слабо полиморфного типа с аннотацией типа. - person octachron; 19.10.2019
comment
просто для ясности, расширение eta - это x => reduceList(minFn, x);, а let max: list(int) => option(int) = reduceList(maxFn); просто удаляет полиморфизм, правильно? - person hesxenon; 19.10.2019
comment
@DanielPotensky Правильно! - person glennsl; 20.10.2019