Мономорфный тип в сопоставлении с шаблоном по умолчанию

Ну, на самом деле это не проблема, так как я решил ее, но это слишком беспокоит меня:

Давайте напишем это:

test.ml

type bop = Beq  | Bneq | Badd
type value = Vint of int | Vchar of char

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

Если я скомпилирую это, я получу:

ocamlc -o test test.ml Файл "test.ml", строка 6, символы 11-62:

Предупреждение 8: это сопоставление с образцом не является исчерпывающим. Вот пример значения, которое не соответствует: Badd

Сборка завершена в вторник 13 сентября 13:24:50

Что нормально, я забыл добавить Badd дело.

Поэтому, поскольку я ненавижу предупреждения, я изменяю свой код на:

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
    | _ -> assert false (* or exit 1 or raise Exit *)
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

А потом я компилирую (и, как вы понимаете, здесь начинается неприятная часть;-)), и я получаю:

ocamlc -o test test.ml Файл "test.ml", строка 13, символы 31-33:

Ошибка: это выражение имеет тип char, но ожидалось выражение типа int

Компиляция вышла ненормально с кодом 2 во вторник 13 сентября 13:26:48

Хорошо что? Я понял, что тип op не было 'a -> 'a -> bool но '_a -> '_a -> bool и поэтому я изменил свой код, потому что вспомнил, что OCaml не допускает полиморфные типы для ненулевых значений и что частичные приложения не являются значениями. Стало:

let eval bop a b = 
  let op a b = match bop with
    | Beq -> a = b 
    | Bneq -> a <> b 
    | _ -> assert false
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

И после того, как я собрал:

ocamlc -o test test.ml

Сборка завершена в вторник 13 сентября 13:29:48

Я мог бы написать:

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
    | _ -> Obj.magic
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

и он тоже отлично компилируется (но, например, Obj.magic - это просто псевдоним для какого-нибудь программиста на OCaml, не так ли?).

Итак, вот мой вопрос, как получилось, что компилятор меняет свое поведение, когда семантически я написал то же самое? (Я тестировал его с несколькими версиями OCaml (3.12.1, 4.01.0, 4.02.3, 4.03.0)).

2 ответа

Решение

Поэтому я предпочитаю ответить простым способом:

приложения мономорфны!

На самом деле, даже лучше:

Если вы не объявление функции, идентификатор или константа, вы не можете быть полиморфными

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

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
    | _ -> fun _ -> assert false
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

и еще одна ссылка, чтобы ответить на это: http://caml.inria.fr/pub/old_caml_site/FAQ/FAQ_EXPERT-eng.html

Отлично, вы только что сняли ограничение по стоимости!

Педагогическое объяснение здесь. Технические подробности здесь.

Другие вопросы по тегам