Подтвердить что ты не робот

Избыточность в декларации типа OCaml (мл/мл)

Я пытаюсь понять специфику модулей ocaml и их компиляции:

am Я вынужден переопределять типы, уже объявленные в .mli внутри конкретных реализаций .ml?

Просто чтобы привести пример:

(* foo.mli *)
type foobar = Bool of bool | Float of float | Int of int

(* foo.ml *)
type baz = foobar option

Это, в соответствии с моим обычным мышлением о интерфейсах/реализациях, должно быть в порядке, но оно говорит

Ошибка: конструктор несвязанного типа foobar

при попытке скомпилировать с помощью

ocamlc -c foo.mli
ocamlc -c foo.ml

Конечно, ошибка исчезает, если я объявляю foobar внутри foo.ml тоже, но это кажется сложным способом, так как я должен держать вещи синхронизированными при каждом изменении.

Есть ли способ избежать этой избыточности или я вынужден каждый раз обновлять типы?

Заранее спасибо

4b9b3361

Ответ 1

OCaml пытается заставить вас отделить интерфейс (.mli) от реализации (.ml). В большинстве случаев это хорошо, для значений вы публикуете тип в интерфейсе и сохраняете код в реализации. Можно сказать, что OCaml обеспечивает определенную абстракцию (интерфейсы должны быть опубликованы, без кода в интерфейсах).

Для типов очень часто реализация такая же, как и интерфейс: оба указывают, что тип имеет определенное представление (и, возможно, объявление типа является генеративным). Здесь не может быть абстракции, потому что у исполнителя нет информации о типе, который он не хочет публиковать. (Исключение составляет в основном, когда вы объявляете абстрактный тип.)

Один из способов взглянуть на это состоит в том, что интерфейс уже содержит достаточно информации для написания реализации. Учитывая интерфейс type foobar = Bool of bool | Float of float | Int of int, существует только одна возможная реализация. Так что не пишите реализацию!

Общей идиомой является наличие модуля, который предназначен для объявлений типа, и сделать его имеющим только .mli. Поскольку типы не зависят от значений, этот модуль обычно находится в самом начале цепи зависимостей. Большинство инструментов компиляции хорошо справляются с этим; например ocamldep будет поступать правильно. (Это одно преимущество перед наличием только .ml.)

Ограничение этого подхода заключается в том, что вам также нужны несколько определений модулей здесь и там. (Типичным примером является определение типа foo, затем OrderedFoo : Map.OrderedType module с type t = foo, затем объявление другого типа с участием 'a Map.Make(OrderedFoo).t.) Они не могут быть помещены в файлы интерфейса. Иногда допустимо разбить ваши определения на несколько кусков, сначала кучу типов (types1.mli), затем модуль (mod1.mli и mod1.ml), затем больше типов (types2.mli). В других случаях (например, если определения являются рекурсивными), вы должны жить либо с .ml без .mli, либо с дублированием.

Ответ 2

Да, вы вынуждены переопределять типы. Единственные способы, которыми я знаю, - это

  • Не используйте файл .mli; просто выставляйте все без интерфейса. Ужасная идея.

  • Используйте инструмент грамотного программирования или другой препроцессор, чтобы избежать дублирования деклараций интерфейса в One True Source. Для больших проектов мы делаем это в своей группе.

Для небольших проектов мы просто дублируем объявления типов. И ворчать об этом.

Ответ 3

Вы можете позволить ocamlc генерировать файл mli для вас из файла ml:

ocamlc -i some.ml > some.mli

Ответ 4

В общем, да, вы должны дублировать типы.

Вы можете обойти это, однако, с помощью Camlp4 и расширения синтаксиса pa_macro (findlib package: camlp4.macro). Он определяет, помимо прочего, конструкцию INCLUDE. Вы можете использовать его для определения общих описаний типов в отдельный файл и включать этот файл в файлы .ml и .mli. Однако я не видел этого в развернутом проекте OCaml, поэтому я не знаю, что он будет квалифицироваться как рекомендуемая практика, но это возможно.

Однако грамотное программирование - это более чистое ИМО.

Ответ 5

Нет, в файле mli просто скажите "тип foobar". Это будет работать.