Module 错误:无法安全地评估递归定义模块的定义

Module 错误:无法安全地评估递归定义模块的定义,module,ocaml,Module,Ocaml,我很想知道为什么会发生这种错误,哪种方法是最好的解决方法 我有两个文件types.ml和types.mli,它们定义了一个变量类型value,可以是许多不同的内置OCaml类型(float、int、list、map、set等) 因为我必须在这个变量类型上使用std库,所以我需要通过functor具体化Set模块,以便能够通过定义ValueSet模块来使用value类型的集合 最后的.ml文件类似于: module rec I : sig type value = Nil | I

我很想知道为什么会发生这种错误,哪种方法是最好的解决方法

我有两个文件
types.ml
types.mli
,它们定义了一个变量类型
value
,可以是许多不同的内置OCaml类型(float、int、list、map、set等)

因为我必须在这个变量类型上使用std库,所以我需要通过functor具体化Set模块,以便能够通过定义
ValueSet
模块来使用
value
类型的集合

最后的
.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)
如您所见,我必须从functor中定义
ValueSet
模块才能使用该数据类型。当我想在
I
的声明中使用该模块时,就会出现问题。因此,我得到以下错误:

错误:无法安全地评估递归定义的模块I的定义

为什么会发生这种情况?解决这个问题的好方法是什么?我只是想知道,我所尝试的方法是否正确?除此之外,它还可以按预期工作(我可以在其他模块中的操作中使用ValueSet类型,但我必须在
types.ml
中注释涉及的行才能通过编译阶段)

我试图删除所有多余的代码,并将代码简化为调查此错误所需的基本代码。。如果还不够,就直接问:)

编辑:根据OCaml参考,我们有

目前,编译器要求递归定义的模块标识符之间的所有依赖循环至少经过一个“安全”模块。如果模块包含的所有值定义都具有函数类型typexpr1->typexpr2,则模块是“安全的”

这是我到目前为止找到的所有东西,但我不明白确切的意思


提前感谢

我真的不确定您在签名中使用了什么样的语法来允许
let..
。我将假设这是一个错误,同时为我们减少了代码。您也不需要该
OrderedType
定义,这可能是我们的另一个摆弄错误,因为您不在Set模块的参数化中使用它

除此之外,我在顶层运行以下内容没有问题。因为这是非常直接的,我不确定你是怎么得到这个错误的

module rec Value :
    sig
        type t =
            | Nil
            | Int       of int
            | Float     of float
            | String    of string
            | Set       of ValueSet.t
        val compare : t -> t -> int 
        val to_string : t -> string
    end = struct
         type t =
            | Nil
            | Int       of int
            | Float     of float
            | String    of string
            | Set       of ValueSet.t

        let compare = Pervasives.compare

        let rec to_string = function
            | Nil -> ""
            | Int x -> string_of_int x
            | Float x -> string_of_float x
            | String x -> x
            | Set l -> 
                Printf.sprintf "{%s} : set" 
                    (ValueSet.fold (fun i v -> v^(to_string  i)^" ") l "")
    end

and ValueSet : Set.S with type elt = Value.t = Set.Make (Value)

在修复了明显的错误之后,您的示例将进行编译(使用OCAML3.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
。因此,编译器在初始化
的代码之前生成初始化
的代码,一切正常

现在试着注释一下
f_empty
的定义

文件“simple.ml”,第11行,字符2-200:

无法安全地评估递归定义的模块值的定义

现在,
确实依赖于
值集
值集
始终依赖于
,因为有
比较
功能。因此,它们是相互递归的,“安全模块”条件必须适用

当前,编译器要求 递归定义的模块标识符至少经过一个“安全”模块。A. 如果模块包含的所有值定义都具有函数类型,则模块是“安全的”
typexpr\u 1->typexpr\u 2

这里,
ValueSet
不安全,因为
ValueSet.empty
Value
不安全,因为
nil

“安全模块”条件的原因是为递归模块选择的实现技术:

对递归模块定义进行求值 通过为涉及的安全模块建立初始值,绑定所有 (功能)值到
fun->raise Undefined\u recursive\u模块
。定义 然后对模块表达式进行求值,并计算安全变量的初始值 模块由这样计算的值替换


如果您在
的签名中注释掉
nil
的声明,则可以将
f_的定义和声明保留为空
。这是因为
Value
现在是一个安全的模块:它只包含函数。在实现中保留
nil
的定义是可以的:
Value
的实现不是一个安全的模块,但是
Value
本身(它的实现被强制为签名)是安全的。

是的,在减少时是一个错误(它是
类型…和Value=
但我删除了其他类型)。事实上没有其他差异,只是检查了一下,所以我真的不知道是什么导致了这个错误。我会更好地检查它并让您知道,同时谢谢。好的,我在顶层尝试了
types.ml
的完整代码,但它不起作用,给出了相同的错误
error:无法安全地评估递归定义的模块I
的定义。。那太傻了。虽然你的简化版有效,但如果我只评论这句话,我的也有效。这很可能是Gilles定义的一个问题;通过将任何常量值转换为单位函数,确保模块安全。