これら 2 つのモジュールに互換性を持たせたい場合がたくさんあります。より単純な使用例は次のとおりです。
module Hashtbl = struct ... (* definition in the stdlib *) end
module ExtHashtbl = struct ... (* my own layer on top of it *) end
ExtHashtbl.t
と互換性を持たせたいHashtbl.t
ので、ExtHashtbl
コードの途中で を使用して機能したり、自分のものではなくライブラリHashtbl.t
についてしか知らない他の誰かによって構築された値を操作したりできます。Hashtbl
ML モジュールの理論には、「強化」と呼ばれる操作があります。これは、モジュール定義を可能な限り多くの方程式で強化し、それらを署名で公開します。アイデアは、より抽象化したい (数式を減らしたい) 場合は、いつでも型シグネチャを使用してそれを制限できるため、数式を使用する方が厳密に一般的であるということです。
ファンクターの場合は状況が少し異なります。A と B を単純なモジュールとして定義する代わりに、それらを空のシグネチャのファンクタにしたと考えてください。
module A (U : sig end) = struct include M end
module B (U : sig end) = struct include M end
ML モジュール システムには、ファンクターの 2 つの異なる概念があります。「ジェネレーティブ」と呼ばれるもの (ファンクターの各呼び出しは、他の呼び出しと互換性のない「新しい」型を生成します) と、「アプリカティブ」と呼ばれるもの (すべて等しい引数でのファンクターの呼び出しには、互換性のある型があります)。OCaml システムは、名前付きのモジュール引数 (より一般的にはpath ) を使用してインスタンス化するとアプリケーション的に動作し、名前のないモジュール引数を使用してインスタンス化すると生成的に動作します。
Xavier Leroy の 2000 年の論文A Modular Module System (PDF) (Web ページA few papers on Camlから)で、OCaml モジュール システムについて知りたいと思っていたよりもはるかに多くのことを学ぶことができます。上記で説明したすべての状況のコード例を以下に示します。
ML モジュール システムに関する最近の研究、特に Anreas Rossberg、Derek Dreyer、Claudio Russo によるものは、「適用的」ファンクターと「生成的」ファンクターの古典的な区別に異なる視点をもたらす傾向があります。彼らは、「純粋な」ファンクターと「不純な」ファンクターに対応する必要があると主張しています。アプリケーションが副作用を実行するファンクターは常に生成的であるべきですが、純粋な項のみをもたらすファンクターはデフォルトでアプリケーション的であるべきです抽象化を提供します)。
module type S = sig
type t
val x : t
end;;
module M : S = struct
type t = int
let x = 1
end;;
(* definitions below are compatible, the test type-checks *)
module A1 = M;;
module B1 = M;;
let _ = (A1.x = B1.x);;
(* definitions below are each independently sealed with an abstract
signature, so incompatible; the test doesn't type-check *)
module A2 : S = M;;
module B2 : S = M;;
let _ = (A2.x = B2.x);;
(*This expression has type B2.t but an expression was expected of type A2.t*)
(* note: if you don't seal Make with the S module type, all functor
applications will be transparently equal to M, and all examples below
then have compatible types. *)
module Make (U : sig end) : S = M;;
(* same functor applied to same argument:
compatible (applicative behavior) *)
module U = struct end;;
module A3 = Make(U);;
module B3 = Make(U);;
let _ = (A3.x = B3.x);;
(* same functor applied to different argument:
incompatible (applicative behavior) *)
module V = struct end;;
module A4 = Make(U);;
module B4 = Make(V);;
let _ = (A4.x = B4.x);;
(* This expression has type B4.t = Make(V).t
but an expression was expected of type A4.t = Make(U).t *)
(* same functor applied to non-path (~unnamed) arguments:
incompatible (generative behavior) *)
module A5 = Make(struct end);;
module B5 = Make(struct end);;
let _ = (A5.x = B5.x);;
(* This expression has type B5.t but an expression was expected
of type A5.t *)