Spec-Zone.ru › OCaml 4.14

10.5 Модули первого класса

(Введены в OCaml 3.12; синтаксис шаблонов и выведение типов пакетов введены в 4.00; структурное сравнение типов пакетов введено в 4.02; с 4.05 требуется меньше скобок)

typexpr ::= ...
∣ (module package-type)
module-expr ::= ...
∣ (val expr [: package-type])
expr ::= ...
∣ (module module-expr [: package-type])
pattern ::= ...
∣ (module module-name [: package-type])
package-type ::= modtype-path
∣ modtype-path with package-constraint { and package-constraint }
package-constraint ::= type typeconstr = typexpr

Модули обычно рассматриваются как статические компоненты. Это расширение позволяет упаковать модуль в качестве значения первого класса, которое позже можно динамически распаковать в модуль.

Выражение ( 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 можно опустить, если его можно вывести из окружающего шаблона.

Синтаксический класс package-type, появляющийся в выражении типа ( module package-type ) и в аннотированных формах, представляет подмножество типов модулей. Это подмножество состоит из именованных типов модулей с необязательными ограничениями ограниченной формы: можно указать только непараметризованные типы.

Для целей проверки типов (и начиная с OCaml 4.02) типы пакетов сравниваются с помощью структурного сравнения типов модулей.

В общем случае выражение модуля ( val expr : package-type ) не может использоваться в теле функтора, так как это может привести к небезопасности в сочетании с прикладными функторами. Начиная с OCaml 4.02, это смягчается двумя способами: если package-type не содержит номинальных объявлений типов (т. е. типов, которые создаются с помощью соответствующей идентификации), то это выражение можно использовать где угодно, и даже если оно содержит такие типы, оно может использоваться внутри тела порождающего функтора, описанного в разделе 10.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-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/firstclassmodules.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API