12.3 Закрытые типы
- 12.3.1 Закрытые типы вариантов и записей
- 12.3.2 Закрытые сокращения типов
- 12.3.3 Закрытые типы строк
Объявления закрытых типов в сигнатурах модулей, вида type t = private ..., позволяют библиотекам раскрывать некоторые, но не все аспекты реализации типа для клиентов библиотеки. В этом отношении они занимают промежуточное положение между абстрактными типами, где никакая информация о реализации типа не раскрывается, и определениями типов данных и сокращениями типов, где все аспекты реализации типа публикуются. Объявления закрытых типов представлены в трех вариантах: для типов вариантов и записей (раздел 12.3.1), для сокращений типов (раздел 12.3.2) и для типов строк (раздел 12.3.3).
12.3.1 Закрытые типы вариантов и записей
(Введено в Objective Caml 3.07)
|
Значения типа варианта или записи, объявленные private, могут быть деструктурированы стандартным образом в совпадении по образцу или с помощью записи expr . field для доступа к полям записей. Однако значения этих типов не могут быть созданы напрямую с помощью применения конструктора или создания записи. Более того, присваивание в изменяемом поле закрытого типа записи запрещено.
Типичное использование закрытых типов — в сигнатуре экспорта модуля, чтобы гарантировать, что создание значений закрытого типа всегда происходит через функции, предоставляемые модулем, при одновременном разрешении совпадения по образцу вне определяющего модуля. Например:
module M : sig type t = private A | B of int val a : t val b : int -> t end = struct type t = A | B of int let a = A let b n = assert (n > 0); B n end
Здесь объявление private гарантирует, что в любом значении типа M.t аргумент конструктора B всегда является положительным целым числом.
По отношению к вариативности их параметров, закрытые типы обрабатываются как абстрактные типы. То есть, если закрытый тип имеет параметры, их вариативность определяется явно, предваряя параметр символом ‘+’ или ‘-’, в противном случае она инвариантна.
12.3.2 Закрытые сокращения типов
(Введено в Objective Caml 3.11)
|
В отличие от обычного сокращения типа, закрытое сокращение типа объявляет тип, который отличается от типа его реализации typexpr. Однако разрешены преобразования из типа к typexpr. Кроме того, компилятор «знает» тип реализации и может использовать эти знания для выполнения оптимизаций, направленных на тип.
Следующий пример использует закрытое сокращение типа для определения модуля неотрицательных целых чисел:
module N : sig type t = private int val of_int: int -> t val to_int: t -> int end = struct type t = int let of_int n = assert (n >= 0); n let to_int n = n end
Тип N.t несовместим с int, что гарантирует, что неотрицательные целые числа и обычные целые числа не смешиваются. Однако, если x имеет тип N.t, преобразование (x :> int) является законным и возвращает базовое целое число, как и N.to_int x. Поддерживаются также глубокие преобразования: если l имеет тип N.t list, преобразование (l :> int list) возвращает список базовых целых чисел, как и List.map N.to_int l, но без копирования списка l.
Обратите внимание, что преобразование ( expr :> typexpr ) на самом деле является сокращенной формой и будет работать только при наличии закрытых сокращений, если ни тип expr, ни typexpr не содержат переменных типа. Если они есть, необходимо использовать полную форму ( expr : typexpr1 :> typexpr2 ), где typexpr1 — ожидаемый тип expr. Конкретно, это будет (x : N.t :> int) и (l : N.t list :> int list) для вышеуказанных примеров.
12.3.3 Закрытые типы строк
(Введено в Objective Caml 3.09)
|
Закрытые типы строк — это сокращения типов, где часть структуры типа остается абстрактной. Конкретно typexpr в приведенном выше случае должна обозначать либо тип объекта, либо полиморфный тип варианта с некоторой возможностью уточнения. Если объявление private используется в интерфейсе, соответствующая реализация может предоставить либо экземпляр, либо уточненный закрытый тип.
module M : sig type c = private < x : int; .. > val o : c end = struct class c = object method x = 3 method y = 2 end let o = new c end
Это объявление делает больше, чем скрывает метод y, оно также делает тип c несовместимым с любым другим закрытым типом объекта, что означает, что только o будет иметь тип c. В этом отношении он ведет себя аналогично закрытым типам записей. Но закрытые типы строк более гибкие в отношении поэтапного уточнения. Эта функция может использоваться в сочетании с функторами.
module F(X : sig type c = private < x : int; .. > end) = struct let get_x (o : X.c) = o#x end module G(X : sig type c = private < x : int; y : int; .. > end) = struct include F(X) let get_y (o : X.c) = o#y end
Полиморфный тип варианта [t], например
type t = [ `A of int | `B of bool ]
может быть уточнен двумя способами. Определение [u] может добавить новое поле к [t], и объявление
type u = private [> t]
оставит эти новые поля абстрактными. Создание значений типа [u] возможно с использованием известных вариантов [t], но любое совпадение по образцу потребует дополнительного случая для обработки потенциальных дополнительных полей. Двойственно, объявление [u] может ограничить поля [t] посредством абстракции: объявление
type v = private [< t > `A]
соответствует закрытым типам вариантов. Невозможно создать значение закрытого типа [v], за исключением использования явно перечисленных присутствующих конструкторов, (`A n) в этом примере; однако при совпадении по образцу на [v] необходимо предполагать, что любой из конструкторов [t] может присутствовать.
Аналогично абстрактным типам, вариативность параметров типа не выводится и должна задаваться явно.
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/privatetypes.html