错误:无法安全评估递归定义模块的定义 [英] Error: Cannot safely evaluate the definition of the recursively-defined module

查看:80
本文介绍了错误:无法安全评估递归定义模块的定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇要理解为什么会发生此错误,这是解决该错误的最佳方法.

我有几个文件types.mltypes.mli,它们定义了一个变体类型value,可以具有许多不同的内置OCaml类型(浮点型,整数型,列表型,映射型,集合型等). /p>

由于必须对此变体类型使用std-lib,因此我需要通过函子具体化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)

如您所见,我必须从函子定义ValueSet模块才能使用该数据类型.当我想在I的声明中使用该模块时,会发生问题.这样我会收到以下错误:

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

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

我试图删除所有多余的代码,并将代码减少为调查此错误所需的基本内容.如果还不够的话,问一下:)

根据OCaml参考,我们有

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

这是我到目前为止找到的所有内容,但我没有确切的含义.

预先感谢

解决方案

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

在表达式级别,模块ValueValueSet不相关.因此,编译器会在初始化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,并且由于compare功能,ValueSet始终依赖于Value.因此它们是相互递归的,因此必须适用安全模块"条件.

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

在这里,由于ValueSet.emptyValueSet不安全,并且由于nilValue不安全.

安全模块"状态的原因是递归模块选择的实现技术:

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

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

I'm curious to understand why this error happens and which is the best way to get around it.

I have a couple of files types.ml and types.mli which define a variant type value that can be of many different builtin OCaml types (float, int, list, map, set, etc..).

Since I have to use the std-lib over this variant type I needed to concretize the Set module through the functor to be able to use sets of value type by defining the ValueSet module.

The final .ml file is something like:

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)

As you can see I had to define the ValueSet module from the functor to be able to use that datatype. The problem occurs when I want to use that module inside the declaration of I. So that I obtain the following error:

Error: Cannot safely evaluate the definition of the recursively-defined module I

Why does this happen? Which is a good way to solve it? And just to know, is my approach to what I'm trying to do correct? Apart from that it works as intended (I'm able to use the ValueSet type with my operations in other modules, but I have to comment the involved line in types.ml to pass compilation phase).

I tried to remove all the superfluous code and reduce the code to essential needed to investigate this error.. if it's not enought just ask :)

EDIT: according to OCaml reference we have that

Currently, the compiler requires that all dependency cycles between the recursively-defined module identifiers go through at least one "safe" module. A module is "safe" if all value definitions that it contains have function types typexpr1 -> typexpr2.

This is everything I found so far, but I don't get the exact meaning..

Thank in advance

解决方案

After fixing the obvious errors, your example does compile (with OCaml 3.10, but I think this hasn't changed since recursive modules were introduced in 3.07). Hopefully my explanations below will help you find what, amongst the definitions you left out, caused your code to be rejected.

Here is some example code that is accepted:

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)

At the expression level, the module Value has no dependency on ValueSet. Therefore the compiler generates the code to initialize Value before the code to initialize Value, and all goes well.

Now try commenting out the definition of f_empty.

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

Now Value does depend on ValueSet, and ValueSet always depends on Value because of the compare function. So they are mutually recursive, and the "safe module" condition must apply.

Currently, the compiler requires that all dependency cycles between the recursively-defined module identifiers go through at least one "safe" module. A module is "safe" if all value definitions that it contains have function types typexpr_1 -> typexpr_2.

Here, ValueSet isn't safe because of ValueSet.empty, and Value isn't safe because of nil.

The reason to the "safe module" condition is the chosen implementation technique for recursive module:

Evaluation of a recursive module definition proceeds by building initial values for the safe modules involved, binding all (functional) values to fun _ -> raise Undefined_recursive_module. The defining module expressions are then evaluated, and the initial values for the safe modules are replaced by the values thus computed.

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.

这篇关于错误:无法安全评估递归定义模块的定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆