モジュールシグネチャ内で type t = private ...
形式で private
な型を宣言すると、ライブラリの利用者に型の実装の一部だけを公開することができるようになります。この点から見ると、 private
な型は抽象型の宣言やデータ型の定義、型略記の間を取ったものになります(抽象型の宣言では型の実装についての情報はまったく公開されませんし、データ型の定義や型略記では型の実装がすべて公開されます)。 private
な型の宣言は、バリアントとレコード型( 7.9.1 節「private
なバリアントとレコード型」)、型略記( 7.9.2 節「private
な型略記」)、列型( 7.9.3 節「private
な列型」)のみっつに分かれます。
(OCaml 3.07 〜)
type-representation | ||
private constr-decl | constr-decl | ||
private field-decl | field-decl |
private
と宣言されたバリアントやレコード型の値は、通常通りパターンマッチングで分解したり expr.field
記法でレコードの要素にアクセスしたりできますが、構成子の適用やレコードの構成により直接これらの値を構成することはできません。さらに、 private
なレコード型の変更可能なフィールドへの代入は認められません。
private
な型の典型的な使用法は、モジュールのシグネチャで使うもので、 private
な型の値は常にそのモジュールで提供される関数で構成されるようにし、かつその一方で、定義したモジュールの外部でもその値にパターンマッチングできるようにする、というものです。例を挙げます。
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
の引数は常に正の整数になることが保証されます。
パラメータの変位については、 private
な型は抽象型と同様に扱われます。すなわち、 private
な型にパラメータがあった場合、変位を指定する場合にはそのパラメータに +
か -
指定することができ、指定しなかった場合には不変になります。
(OCaml 3.11 〜)
type-equation | ||
private typexpr |
通常の型略記とは異なり、 private
な型略記は実装の型 typexpr とは区別される型を宣言します。ただし、宣言した型から typexpr へ型変換することはできます。さらに、コンパイラは実装の型が何であるか「知っている」ので、そのことを使って型情報を利用した最適化を行なうこともできます。曖昧さのため、 typexpr としてオブジェクトや多相バリアント型を指定することはできませんが、同様のことは private
な列型を使えば実現できます。
以下の例では private
な型略記を使って非負整数のモジュールを定義しています。
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
とした場合と同じように、内部で使われている整数をリストにしたものが返ります(ただし、 List.map N.to_int
の場合とは異なり、リスト l
はコピーされません)。
型変換 (expr :> typexpr)
は略記形で、 private
な型略記の存在下ではexpr の型と typexpr のどちらにも型変数が含まれない場合にだけ動作することに注意してください。それ以外の場合には、非省略形の (expr : typ_e :> typexpr)
を使わなければなりません(typ_e
は expr に期待される型です)。具体的に言えば、上の例では (x : N.t :> int)
や (l : N.t list :> int list)
のようになります。
(OCaml 3.09 〜)
type-equation | ||
private typexpr |
private
な列型は型構造の一部分が抽象的になる型略記です。具体的には、 typexpr は、オブジェクト型か多相バリアント型で、詳細化の余地のあるものでなければなりません。インタフェース中で private
宣言が使われた場合、対応する実装は、基底となる実装か 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
型になるということです。この意味では、 private
な列型の振る舞いは private
なレコード型と同じです。しかし private
な列型は段階的な詳細化に関してより柔軟です。この機能はファンクタと組み合わせて使うことができます。
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
多相バリアント型は、新たな構成子を追加するか、宣言された構成子を削除できるようにするかのふた通りの方法で詳細化することができます。後者は private
なバリアント型に対応し(private
な型の値を作成することができない)、前者では、追加された構成子を扱うためにパターンマッチに default case を持たせておく必要があります。
type t = [ `A of int | `B of bool ] type u = private [< t > `A ] type v = private [> t ]
型 u
では (`A n)
として値を作成することができますが、 (`B b)
とすることはできません。型 v
では値の作成に制限はありませんが、パターンマッチに default case がなければなりません。
抽象型や private
な型と同じく、型パラメータの変位は推論されず、明示しなければなりません。