Spec-Zone.ru › OCaml 5.0

12.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 не содержит именованных объявлений типов (т.е. типов, созданных с помощью собственного идентификатора), то это выражение может быть использовано где угодно, и даже если оно содержит такие типы, оно может использоваться внутри тела генеративного функтора, описанного в разделе 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-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/firstclassmodules.html

Spec-Zone.ru

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