7.8 再帰モジュール

(OCaml 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 }

再帰モジュールの定義は module rec ... and ... 構文で始めます。これは、通常の module module-name = module-expr 形式のモジュール定義と module module-name : module-type 形式のモジュール仕様を一般化し、 module-exprmodule-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 の実験的な拡張です。受け入れられる再帰的定義のクラスは、その動的な意味論も含め、最終的なものではなく将来変更されるかもしれません。

現在の実装では、再帰的に定義されたモジュール識別子の依存関係の輪が、最低でもひとつは「安全な」モジュールを含むことを要求しています。モジュール中のすべての値の定義が関数型 typexpr1 -> typexpr2 を持つとき、そのモジュールは「安全」である、と言います。再帰モジュールの定義は、そこに含まれる安全なモジュール内の(関数)値に初期値として fun _ -> raise Undefined_recursive_module を束縛することにより進められます。その次に、定義中のモジュール内の式を評価し、それによって計算された値で安全なモジュール内の束縛に与えられた初期値が置き換えられます。安全なモジュール内の関数が値の計算中に適用されると(これは ill-founded な再帰定義にあたります)、 Undefined_recursive_module 例外が発生します。