8.2 Recursive modules

(Introduced in Objective Caml 3.07)

definition::=
module rec module-name : module-type = module-expr { and module-name : module-type = module-expr }
specification::=
module rec module-name : module-type { and module-name: module-type }

Recursive module definitions, introduced by the module rec …and … construction, generalize regular module definitionsmodulemodule-name= module-expr and module specificationsmodulemodule-name: module-type by allowing the definingmodule-expr and the module-type to refer recursively to the moduleidentifiers being defined. A typical example of a recursive moduledefinition is:

  1. module rec A : sig
  2. type t = Leaf of string | Node of ASet.t
  3. val compare: t -> t -> int
  4. end = struct
  5. type t = Leaf of string | Node of ASet.t
  6. let compare t1 t2 =
  7. match (t1, t2) with
  8. | (Leaf s1, Leaf s2) -> Stdlib.compare s1 s2
  9. | (Leaf _, Node _) -> 1
  10. | (Node _, Leaf _) -> -1
  11. | (Node n1, Node n2) -> ASet.compare n1 n2
  12. end
  13. and ASet
  14. : Set.S with type elt = A.t
  15. = Set.Make(A)
  16.  

It can be given the following specification:

  1. module rec A : sig
  2. type t = Leaf of string | Node of ASet.t
  3. val compare: t -> t -> int
  4. end
  5. and ASet : Set.S with type elt = A.t
  6.  

This is an experimental extension of OCaml: the class ofrecursive definitions accepted, as well as its dynamic semantics arenot final and subject to change in future releases.

Currently, the compiler requires that all dependency cycles betweenthe recursively-defined module identifiers go through at least one“safe” module. A module is “safe” if all value definitions thatit contains have function types typexpr1-> typexpr2. Evaluation of arecursive module definition proceeds by building initial values forthe safe modules involved, binding all (functional) values tofun_->raiseUndefined_recursive_module. The definingmodule expressions are then evaluated, and the initial valuesfor the safe modules are replaced by the values thus computed. If afunction component of a safe module is applied during this computation(which corresponds to an ill-founded recursive definition), theUndefined_recursive_module exception is raised at runtime:

  1. module rec M: sig val f: unit -> int end = struct let f () = N.x end
  2. and N:sig val x: int end = struct let x = M.f () end
  3. Exception: Undefined_recursive_module ("exten.etex", 1, 43).

If there are no safe modules along a dependency cycle, an error is raised

  1. module rec M: sig val x: int end = struct let x = N.y end
  2. and N:sig val x: int val y:int end = struct let x = M.x let y = 0 end
  3. Error: Cannot safely evaluate the definition of the following cycle
  4. of recursively-defined modules: M -> N -> M.
  5. There are no safe modules in this cycle (see manual section 8.2).
  6. File "exten.etex", line 1, characters 18-28:
  7. 1 | module rec M: sig val x: int end = struct let x = N.y end
  8. ^^^^^^^^^^
  9. Module M defines an unsafe value, x .
  10. File "exten.etex", line 2, characters 10-20:
  11. 2 | and N:sig val x: int val y:int end = struct let x = M.x let y = 0 end
  12. ^^^^^^^^^^
  13. Module N defines an unsafe value, x .

Note that, in the specification case, the module-types must beparenthesized if they use the withmod-constraint construct.