我很想知道为什么会出现这种错误,哪种方法可以解决它.
我有几个文件types.ml,types.mli它们定义了一个变体类型value,可以有许多不同的内置OCaml类型(float,int,list,map,set等).
由于我必须在这个变体类型上使用std-lib,我需要通过仿函数将Set模块具体化,以便能够value通过定义ValueSet模块来使用类型集.
最终.ml文件类似于:
module rec I :
sig
type value =
Nil
| Int of int
| Float of float
| Complex of Complex.t
| String of string
| List of (value list) ref
| Array of value array
| Map of (value, value) Hashtbl.t
| Set of ValueSet.t ref
| Stack of value Stack.t
...
type t = value
val compare : t -> t -> int
end
= struct
(* same variant type *)
and string_value v =
match v with
(* other cases *)
| Set l -> sprintf "{%s} : set" (ValueSet.fold (fun i v -> v^(string_value i)^" ") !l "")
end
and OrderedValue :
sig
type t = I.value
val compare : t -> t -> int
end
= struct
type t = I.value
let compare = Pervasives.compare
end
and ValueSet : Set.S with type elt = I.value = Set.Make(I)
Run Code Online (Sandbox Code Playgroud)
如您所见,我必须ValueSet从仿函数定义模块才能使用该数据类型.当我想在声明中使用该模块时,会出现问题I.这样我就得到以下错误:
错误:无法安全地评估递归定义模块I的定义
为什么会这样?哪个是解决它的好方法?只是要知道,我正在努力做到正确的方法吗?除此之外它按预期工作(我可以在其他模块中使用我的操作的ValueSet类型,但我必须注释所涉及的行types.ml以通过编译阶段).
我试图删除所有多余的代码,并将代码减少到调查此错误所需的必要条件..如果它不是只需要问:)
编辑:根据OCaml参考我们有
目前,编译器要求递归定义的模块标识符之间的所有依赖循环都通过至少一个"安全"模块.如果模块包含的所有值定义都具有typexpr1 - > typexpr2的函数类型,则该模块是"安全的".
这是我到目前为止发现的一切,但我没有得到确切含义..
预先感谢
修复明显的错误后,您的示例确实可以编译(使用 OCaml 3.10,但我认为自从 3.07 中引入递归模块以来,这一点没有改变)。希望我下面的解释能够帮助您找到您遗漏的定义中导致代码被拒绝的原因。
\n\n以下是一些被接受的示例代码:
\n\nmodule rec Value : sig\n type t =\n Nil\n | Set of ValueSet.t \n val compare : t -> t -> int\n val nil : t\n (*val f_empty : unit -> t*)\nend\n= struct\n type t =\n Nil\n | Set of ValueSet.t\n let compare = Pervasives.compare\n let nil = Nil\n (*let f_empty () = Set ValueSet.empty*) \nend\nand ValueSet : Set.S with type elt = Value.t = Set.Make(Value)\nRun Code Online (Sandbox Code Playgroud)\n\n在表达式级别,该模块Value不依赖于ValueSet. 因此,编译器在要初始化的Value代码之前生成要初始化的代码Value,一切顺利。
现在尝试注释掉 的定义f_empty。
\n\n\n\n
File "simple.ml", line 11, characters 2-200:
\nCannot safely evaluate the definition of the recursively-defined module Value
现在Value确实依赖于ValueSet,并且由于函数的原因ValueSet始终依赖于。因此它们是相互递归的,并且 \xe2\x80\x9csafe module\xe2\x80\x9d 条件必须适用。Valuecompare
\n\n\n目前,编译器要求递归定义的模块标识符之间的所有依赖循环至少经过一个“安全”模块。如果模块包含的所有值定义都具有函数类型,则该模块是“安全的”
\ntypexpr_1 -> typexpr_2。
在这里,ValueSet因为 不安全ValueSet.empty,Value因为 不安全nil。
\xe2\x80\x9csafe module\xe2\x80\x9d 条件的原因是递归模块选择的实现技术:
\n\n\n\n\n递归模块定义的评估通过为所涉及的安全模块构建初始值、将所有(功能)值绑定到 来进行
\nfun _ -> raise Undefined_recursive_module。然后对定义的模块表达式进行求值,并用由此计算出的值替换安全模块的初始值。
如果注释掉nil签名中的声明Value,则可以保留 的定义和声明f_empty。那是因为Value现在是一个安全模块:它只包含函数。可以将 的定义保留nil在实现中: 的实现Value不是安全模块,但Value其本身(这是其强制签名的实现)是安全的。