修复明显的错误后,您的示例确实可以编译(使用 OCaml 3.10,但我认为自从 3.07 中引入递归模块以来,这一点没有改变)。希望我下面的解释能够帮助您找到您遗漏的定义中导致代码被拒绝的原因。
以下是一些被接受的示例代码:
module rec Value : sig
type t =
Nil
| Set of ValueSet.t
val compare : t -> t -> int
val nil : t
(*val f_empty : unit -> t*)
end
= struct
type t =
Nil
| Set of ValueSet.t
let compare = Pervasives.compare
let nil = Nil
(*let f_empty () = Set ValueSet.empty*)
end
and ValueSet : Set.S with type elt = Value.t = Set.Make(Value)
在表达水平上,该模块Value
不依赖于ValueSet
。因此编译器生成代码来初始化Value
在初始化代码之前Value
,一切顺利。
现在尝试注释掉 的定义f_empty
.
File "simple.ml", line 11, characters 2-200:
Cannot safely evaluate the definition of the recursively-defined module Value
Now Value
确实取决于ValueSet
, and ValueSet
总是取决于Value
因为compare
功能。因此它们是相互递归的,并且“安全模块”条件必须适用。
目前,编译器要求所有依赖关系之间的循环
递归定义的模块标识符至少要经过一个“安全”模块。 A
如果模块包含的所有值定义都具有函数类型,则该模块是“安全的”typexpr_1 -> typexpr_2
.
Here, ValueSet
不安全,因为ValueSet.empty
, and Value
不安全,因为nil
.
“安全模块”条件的原因是递归模块选择的实现技术:
递归模块定义的评估继续进行
通过为所涉及的安全模块构建初始值,绑定所有
(功能)值fun _ -> raise Undefined_recursive_module
。定义
然后评估模块表达式,并计算安全的初始值
模块被由此计算的值替换。
如果你注释掉声明nil
在签名中Value
,你可以留下定义和声明f_empty
。那是因为Value
现在是一个安全模块:它只包含函数。留下定义就可以了nil
在实施中:实施Value
不是一个安全模块,但是Value
其本身(强制签名的实现)是安全的。