Сопоставление с образцом GADT

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

Я видел здесь несколько других ответов на сопоставление с образцом GADT, но, похоже, это немного другое.

Я видел, как это делается для представления типа без возможных значений:

module Nothing = {
  type t =
    | Nothing(t);
};

Поэтому я хотел использовать его, чтобы заблокировать этот тип Exit.t, чтобы у меня был тип Exit.t ('a, Nothing.t) для представления случая Success, фиксирующего тот факт, что нет восстанавливаемого значения Failure.

module Exit = {
  type t('a, 'e) =
    | Success('a): t('a, Nothing.t)
    | Failure('e): t(Nothing.t, 'e);

Это казалось нормальным, пока я не попытался написать для него функцию flatMap.

  let flatMap: ('a => t('a1, 'e), t('a, 'e)) => t('a1, 'e) = (f, exit) =>
    switch (exit) {
    | Success(a) => f(a)
    | Failure(_) as e => e
    };
};

Как есть, он подразумевает, что тип Exit.t всегда будет Exit.t (Nothing.t, Nothing.t), что, как я понимаю, поскольку тип в случае Failure установит для первого типа значение Nothing, а в случае Success будет установите для второго типа значение Nothing.

Я попробовал одну вещь, которую знаю, - сделать некоторые из этих типов локальными с помощью type a. Я пробовал type a a1 e и type a e оставить 'a1, но, похоже, мне просто не удалось уловить идею.


person user1739571    schedule 29.06.2020    source источник
comment
Я не понимаю, как бы вы это использовали. Если вы используете любой из конструкторов, вы заставите одну или другую переменную типа быть Nothing.t во время компиляции. Ничего другого и быть не может. И если это так, почему бы просто не вернуть значение напрямую? Я предполагаю, что у вас есть какой-то вариант использования для этого, но я не могу понять, что это из этого (это часто называется XY проблема)   -  person glennsl    schedule 29.06.2020
comment
это было разведочным. Я пытался реализовать этот тип, чтобы перенести некоторый код Scala на OCaml.   -  person user1739571    schedule 29.06.2020


Ответы (1)


(Я использую синтаксис OCaml ниже, поскольку вопрос также помечен тегом ocaml, но то же самое должно относиться к Reason.)

Во-первых, ваш тип Nothing.t не пустой. Циклическое значение Nothing (Nothing (Nothing (...))) - действительный житель. Если вы действительно хотите, чтобы тип был пустым, не помещайте конструктор.

Во-вторых, как вы догадались, в flat_map ваша ветка Failure принудительно создает экземпляр 'a1 с Nothing.t. Нет никакого способа обойти это; это не недостаток компилятора, а единственный способ интерпретировать этот код.

В-третьих, вы слишком усложняете ситуацию, поскольку Exit.t не обязательно должен быть GADT для достижения ваших целей.

Ниже приведен более простой пример, который показывает, что если Nothing.t на самом деле пусто, то компилятор правильно разрешает нерелевантные переходы.

type nothing = |
type ('a, 'b) result =
  | Success of 'a
  | Failure of 'b
let only_success (x : ('a, nothing) result) : 'a =
  match x with
  | Success v -> v
  | Failure _ -> . (* this branch can be removed, as it is correctly inferred *)
person Guillaume Melquiond    schedule 29.06.2020