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 }
再帰的モジュール定義は、 'module rec' ... 'and' ... 構文で導入できますが, これは通常のモジュール定義 module module-name = module-expr と モジュール仕様 module module-name : module-type を、module-expr や module-type で定義途中のモジュール識別子を参照できるように一般化したものです。 典型的な再帰的モジュール定義は:
module rec 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)
これには 次の仕様を与えることができます:
module rec 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
これは Objective Caml の実験的な拡張です: 許可される再帰的定義のクラスは、その動的な意味論と同様に、最終的なものではなく将来のリリースで変更されるかもしれません。
今のところ、 コンパイラは 再帰的に定義されたモジュール識別子のあいだのすべての依存関係のサイクルが最低1つの「安全な」モジュールを通ることを要求します。 モジュールが安全であるとは、 モジュールが含んでいるすべての値定義が関数型 typexpr_1 -> typexpr_2 をもつことです。 再帰的モジュールの評価は 安全なモジュールの初期値を構築することで進行し, 全ての (関数)値 を fun _ -> raise Undefined_recursive_module に束縛します. その後にモジュール式全体が評価され,安全なモジュールの初期値がここで計算された値で置き換えられます. もしある安全なモジュールに含まれる関数がこの計算の途中で適用された場合 (これは ill-founded (FIXME:日本語訳) な再帰的定義に対応します)、 Undefined_recursive_module 例外が raise されます。