7

我很想知道为什么会发生这个错误,这是解决它的最佳方法。

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

由于我必须在这个变体类型上使用标准库,我需要通过函子具体化 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)

如您所见,我必须ValueSet从仿函数定义模块才能使用该数据类型。当我想在I. 这样我得到以下错误:

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

为什么会这样?哪个是解决它的好方法?只是想知道,我尝试做的事情的方法是否正确?除此之外,它按预期工作(我可以在其他模块中将 ValueSet 类型与我的操作一起使用,但我必须在其中注释所涉及的行types.ml以通过编译阶段)。

我试图删除所有多余的代码并将代码减少到调查此错误所需的必要代码..如果还不够,请询问:)

编辑:根据 OCaml 参考我们有

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

这是我到目前为止发现的所有内容,但我不明白确切的含义..

预先感谢

4

2 回答 2

3

在修复了明显的错误之后,您的示例确实可以编译(使用 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

现在Value确实依赖于ValueSet,并且ValueSet总是依赖于Value函数compare。所以它们是相互递归的,“安全模块”条件必须适用。

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

在这里,不安全,ValueSet因为ValueSet.emptyValue也不安全,因为nil

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

递归模块定义的评估通过为所涉及的安全模块构建初始值,将所有(功能)值绑定到fun _ -> raise Undefined_recursive_module. 然后评估定义模块表达式,并将安全模块的初始值替换为由此计算的值。

If you comment out the declaration of nil in the signature of Value, you can leave the definition and declaration of f_empty. That's because Value is now a safe module: it contains only functions. It's ok to leave the definition of nil in the implementation: the implementation of Value is not a safe module, but Value itself (which is its implementation coerced to a signature) is safe.

于 2010-11-21T22:51:35.517 回答
2

我真的不确定您在允许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)
于 2010-11-21T19:02:49.057 回答