代码之家  ›  专栏  ›  技术社区  ›  Jack

错误:无法安全地计算递归定义的模块的定义

  •  6
  • Jack  · 技术社区  · 14 年前

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

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

    因为我必须在这个变量类型上使用std库,所以我需要通过函子将Set模块具体化,以便能够使用 价值 通过定义 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)
    

    如你所见,我必须定义 值集 函数中的模块,以便能够使用该数据类型。当我想在 I . 从而得到以下错误:

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

    为什么会这样?哪一个是解决问题的好方法?只是想知道,我的方法是正确的吗?除此之外,它按预期工作(我可以在其他模块中的操作中使用ValueSet类型,但是我必须在 类型.ml 通过编译阶段)。

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

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

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

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

    提前感谢

    2 回复  |  直到 14 年前
        1
  •  3
  •   Gilles 'SO- stop being evil'    14 年前

    下面是一些可以接受的示例代码:

    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 .

    File "simple.ml", line 11, characters 2-200:
    Cannot safely evaluate the definition of the recursively-defined module Value

    现在 价值 取决于 值集 ,和 值集 总是取决于 价值 因为 compare 功能。因此它们是相互递归的,必须应用安全模块条件。

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

    在这里, 值集 不安全是因为 ValueSet.empty ,和 价值 不安全是因为 nil .

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

    递归模块定义的求值过程 通过为所涉及的安全模块生成初始值,绑定所有 (功能)值 fun _ -> raise Undefined_recursive_module . 定义 然后对模块表达式求值,并对safe的初始值 用由此计算的值替换模块。

    如果你对 签字人 价值 ,您可以保留 空的 . 那是因为 价值 现在是一个安全的模块:它只包含函数。把这个定义留给 在实施中:实施 价值 不是安全模块,但是 价值 它本身(它的实现强制为签名)是安全的。

        2
  •  2
  •   nlucaroni    14 年前

    我真的不确定你在签名中使用了什么样的语法 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)