Spec-Zone.ru › OCaml 5.0

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)

type-representation ::= ...
∣ = private [ | ] constr-decl { | constr-decl }
∣ = private record-decl

Значения типа варианта или записи, объявленные 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)

type-equation ::= ...
∣ = private typexpr

В отличие от обычного сокращения типа, закрытое сокращение типа объявляет тип, который отличается от типа его реализации 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)

type-equation ::= ...
∣ = private typexpr

Закрытые типы строк — это сокращения типов, где часть структуры типа остается абстрактной. Конкретно 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

Spec-Zone.ru

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