Глава 12 Расширения языка
3 Приватные типы
Объявления приватных типов в сигнатурах модулей вида type t = private ... позволяют библиотекам раскрывать клиентам библиотеки некоторые, но не все аспекты реализации типа. В этом отношении они занимают промежуточное положение между объявлениями абстрактных типов, не раскрывающими никаких сведений о реализации типа, и определениями типов данных и сокращениями типов, полностью раскрывающими реализацию типа. Существует три разновидности объявлений приватных типов: для вариантов и типов записей (раздел 12.3.1), для сокращений типов (раздел 12.3.2) и для строковых типов (раздел 12.3.3).
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 всегда является положительным целым числом.
С точки зрения дисперсии параметров с приватными типами обращаются так же, как с абстрактными типами. То есть, если у приватного типа есть параметры, их дисперсия задается явно с помощью префикса «+» или «-» перед параметром; в противном случае она инвариантна.
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).
3.3 Приватные строковые типы
(Добавлено в Objective Caml 3.09)
|
Приватные строковые типы — это сокращения типов, в которых часть структуры типа остается абстрактной. В частности, typexpr в приведенном выше примере должен обозначать либо тип объекта, либо полиморфный вариантный тип, допускающий некоторую степень уточнения. Если приватное объявление используется в интерфейсе, соответствующая реализация может предоставить либо конкретный экземпляр, либо уточненный приватный тип.
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-2026 INRIA.
https://ocaml.org/manual/5.5/privatetypes.html