OCaml の再帰モジュール

Recursive モジュールは OCaml の興味深い機能です。これを使用するには、フォームを使用する必要があります

module rec module-name : module-signature = module-expr

再帰モジュールを使用する場合は、明示的な署名が必要です。これは、コンパイラが再帰を使用してモジュール署名を推測できなくなったためです。

典型的な再帰モジュールは次のようになります:

module rec M : sig
  (* explicit signature *)
end = struct
  (* Implementations *)
end

また、相互に再帰的なモジュールを使用することもできます。例:

module rec A : sig ...end = struct ... end
and B : sig ... end = struct ... end

再帰モジュールの私の主な使用例は、ファーストクラスのモジュールと結合することです.ファーストクラスのモジュールは、モジュールをラップする通常の値です.これは、OCaml に動的ポリモーフィズムを導入する強力な方法です.

動的ポリモーフィズムは通常、再帰的なデータ型と組み合わされますが、Ocaml モジュールはデフォルトでは再帰的ではありません。したがって、再帰的なモジュールは貴重な追加機能として機能します。

たとえば、私は自分の ocamlpt プロジェクトでファーストクラス モジュールと再帰モジュールを使用しています。

module type Shape = sig
  type t
  val hit: Ray.t -> t -> Material.hit_record option
  val bounding_box: t -> Aabb.t
end

形状をポリモーフィックにしたいので、ファーストクラスのモジュールを使用する必要があります。以下のコードでは、Shape_instance を導入しています。 形状モジュールとそのモジュールの値の両方をラップするモジュール、および build_shape も追加します Shape_instance の署名のファーストクラス モジュールを構築する関数 .このようにして、これらのファーストクラスのモジュールを保存できます。それらを使用したいときはいつでも、ファーストクラスのモジュールをアンラップして、具体的な Shape_instance を取得できます。 モジュール。

module type Shape_instance = sig
  module S: Shape
  val this: S.t
end

let build_shape
    (type a)
    (module S : Shape with type t = a)
    (shape: a)
  =
  (module struct
    module S = S
    let this = shape
  end : Shape_instance
  )

上記のコードは、球や三角形などの具体的な形状を処理するのに十分です.ただし、形状は、バウンディング ボリューム階層 (BVH) と呼ばれるツリー構造で編成されています.また、各 BVH ノードには、BVH ノード自体を含む他の形状を含めることができます.

再帰モジュールを使用して BVH ノードを実装できます:

module rec Bvh_node : sig
  include Shape
  val create: (module Shape_instance) list -> t
end = struct

type t = {
  left: (module Shape_instance);
  right: (module Shape_instance);
  aabb: Aabb.t;
}

(* Other members of the Bvh_node module *)

(* Creating bvh code from a list of objects *)
let rec create (shapes: (module Shape_instance) list) =
  ...
  (* if shapes contain 3 elements *)
  let left = ...
  and right = ... in
  let aabb = Aabb.union left.aabb right.aabb in
  { left=(build_shape (module Bvh_node) left);
    right=(build_shape (module Bvh_node) right);
    aabb }

end

上記のコードは魔法のように機能しますが、コードは rec なしではコンパイルできません。 Bvh_node の前のキーワード create 以降 関数はモジュール Bvh_node を参照します

全体として、再帰モジュールは、純粋な階層モジュール システムではサポートできないコンポーネント間の循環依存をサポートする方法です。このような循環依存は通常望ましくなく、ソフトウェア設計を変更することで回避できます。それでも、モジュールを使用する正当な理由がある場合もあります。特に、OCaml モジュール システムの汎用性を考慮してください。そのような場合、再帰モジュールは貴重な資産として機能します。