Private types are variant or record types. Values of
these types can be de-structured normally in pattern-matching or via
the expr . field notation for record accesses. However, values of
these types cannot be constructed directly by constructor application
or record construction. Moreover, assignment on a mutable field of a
private record type is not allowed.
The typical use of private types is in the export signature of a
module, to ensure that construction of values of the private type always
go through the functions provided by the module, while still allowing
pattern-matching outside the defining module. For example:
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
Here, the private declaration ensures that in any value of type
M.t, the argument to the B constructor is always a positive integer.
Recursive module definitions, introduced by the 'module rec' ...'and' ... construction, generalize regular module definitions
module module-name = module-expr and module specifications
module module-name : module-type by allowing the defining
module-expr and the module-type to refer recursively to the module
identifiers being defined. A typical example of a recursive module
definition is:
module A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
= struct
type t = Leaf of string | Node of ASet.t
let compare t1 t2 =
match (t1, t2) with
(Leaf s1, Leaf s2) -> Pervasives.compare s1 s2
| (Leaf _, Node _) -> 1
| (Node _, Leaf _) -> -1
| (Node n1, Node n2) -> ASet.compare n1 n2
end
and ASet : Set.S with type elt = A.t
= Set.Make(A)
It can be given the following specification:
module A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
and ASet : Set.S with type elt = A.t
This is an experimental extension of Objective Caml: the class of
recursive definitions accepted, as well as its dynamic semantics are
not final and subject to change in future releases.
Currently, the compiler requires that all dependency cycles between
the recursively-defined module identifiers go through at least one
``safe'' module. A module is ``safe'' if all value definitions that
it contains have function types ty1 -> ty2. Evaluation of a
recursive module definition proceeds by building initial values for
the safe modules involved, binding all (functional) values to
fun x -> raise Undefined_recursive_module. The defining
module expressions are then evaluated, and the initial values
for the safe modules are replaced by the values thus computed. If a
function component of a safe module is applied during this computation
(which corresponds to an ill-founded recursive definition), the
Undefined_recursive_module exception is raised.