Глава 12 Расширения языка
5 Модули первого класса
(Введены в OCaml 3.12; синтаксис шаблонов и вывод типов пакетов введены в 4.00; структурное сравнение типов пакетов введено в 4.02.; уменьшенное количество скобок с 4.05)
|
Модули обычно рассматриваются как статические компоненты. Это расширение позволяет упаковать модуль в качестве значения первого класса, которое позже может быть динамически распаковано в модуль.
Выражение ( module module-expr : package-type ) преобразует модуль (структуру или функтор), обозначенный выражением модуля module-expr, в значение ядра языка, которое инкапсулирует этот модуль. Тип этого значения ядра языка — ( module package-type ). Аннотация package-type может быть опущена, если её можно вывести из контекста.
Обратно, выражение модуля ( val expr : package-type ) оценивает выражение ядра языка expr до значения, которое должно иметь тип module package-type, и извлекает модуль, который был инкапсулирован в этом значении. Опять же, package-type может быть опущено, если тип expr известен. Если выражение модуля уже заключено в скобки, например, аргументы функторов, дополнительные скобки не нужны: Map.Make(val key).
Шаблон ( module module-name : package-type ) сопоставляет пакет с типом package-type и связывает его с module-name. Он не разрешён в связываниях let на верхнем уровне.
Класс синтаксических конструкций package-type, встречающийся в выражении типа ( module package-type ) и в аннотированных формах, представляет собой подмножество типов модулей. Это подмножество состоит из именованных типов модулей с необязательными ограничениями ограниченной формы: можно указать только непараметризованные типы.
Для целей проверки типов (и начиная с OCaml 4.02) типы пакетов сравниваются с использованием структурного сравнения типов модулей.
В общем случае, выражение модуля ( val expr : package-type ) не может быть использовано в теле функтора, так как это может привести к некорректности в сочетании с прикладными функторами. С OCaml 4.02 это ослаблено в двух аспектах: если package-type не содержит именованных объявлений типов (типы, созданные с соответствующей идентичностью), то это выражение может использоваться в любом месте, а если оно содержит такие типы, то оно может использоваться в теле порождающего функтора, описанного в разделе 12.15. Оно также может использоваться в любом контексте локального связывания модулей let module module-name = ( val expr1 : package-type ) in expr2.
Базовый пример
Типичное использование модулей первого класса — это выбор в момент выполнения среди нескольких реализаций сигнатуры. Каждая реализация — это структура, которую можно инкапсулировать как модуль первого класса, а затем хранить в структуре данных, такой как хеш-таблица:
type picture = … module type DEVICE = sig val draw : picture -> unit … end let devices : (string, (module DEVICE)) Hashtbl.t = Hashtbl.create 17 module SVG = struct … end let _ = Hashtbl.add devices "SVG" (module SVG : DEVICE) module PDF = struct … end let _ = Hashtbl.add devices "PDF" (module PDF : DEVICE)
Затем можно выбрать одну реализацию на основе аргументов командной строки, например:
let parse_cmdline () = …
module Device =
(val (let device_name = parse_cmdline () in
try Hashtbl.find devices device_name
with Not_found ->
Printf.eprintf "Unknown device %s\n" device_name;
exit 2)
: DEVICE)
Или выбор можно выполнить внутри функции:
let draw_using_device device_name picture =
let module Device =
(val (Hashtbl.find devices device_name) : DEVICE)
in
Device.draw picture
Расширенные примеры
С модулями первого класса можно параметризовать код относительно реализации модуля без использования функтора.
let sort (type s) (module Set : Set.S with type elt = s) l = Set.elements (List.fold_right Set.add l Set.empty) val sort : (module Set.S with type elt = 's) -> 's list -> 's list =
Для использования этой функции можно обернуть функтор Set.Make:
let make_set (type s) cmp =
let module S = Set.Make(struct
type t = s
let compare = cmp
end) in
(module S : Set.S with type elt = s)
val make_set : ('s -> 's -> int) -> (module Set.S with type elt = 's) =
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/firstclassmodules.html