Мономорфный тип в сопоставлении с шаблоном по умолчанию
Ну, на самом деле это не проблема, так как я решил ее, но это слишком беспокоит меня:
Давайте напишем это:
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