2010-11-21 57 views
6

我很想知道爲什麼會發生此錯誤,以及哪種方法是解決此問題的最佳方法。錯誤:無法安全地評估遞歸定義模塊的定義

我有幾個文件types.mltypes.mli它定義一個變量類型value,可以有多種不同的內置OCaml的類型(浮動,INT,列表,地圖,設置,等等)的。

因爲我必須在這個變體類型上使用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聲明中使用該模塊時,會出現問題。因此,我獲得以下錯誤:

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

爲什麼會發生這種情況?哪種解決方法是一個好方法?只是要知道,我的方法是什麼,我試圖做的是正確的?除此之外,它按預期工作(我可以在其他模塊中將ValueSet類型與我的操作一起使用,但我必須對types.ml中涉及的行進行註釋以通過編譯階段)。

我試圖刪除所有多餘的代碼,減少代碼必不可少的需要調查這個錯誤..如果它沒有足夠多隻問:)

編輯:根據OCaml的參考,我們有

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.

這一切,我發現,到目前爲止,但我沒有得到確切的意思..

預先感謝

回答

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) 

在表達水平,該模塊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不依賴於ValueSetValueSet總是取決於Value因爲compare功能。所以它們是相互遞歸的,並且必須應用「安全模塊」條件。

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 .

這裏,ValueSet是不是因爲ValueSet.empty安全,Value是不是因爲nil安全。

的原因「安全模塊」的條件是遞歸的模塊選擇的實現技術:

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.

如果您在Value簽名註釋掉的nil的聲明,你可以離開的定義和聲明f_empty。這是因爲Value現在是一個安全的模塊:它只包含功能。在執行中保留nil的定義是可以的:Value的實現不是一個安全模塊,但是Value本身(這是它的實現被強制爲簽名)是安全的。

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) 
+0

是的,這是一個錯誤,而減少(這是一個'類型...和值=',但我刪除了其他類型)。其實沒有其他區別,只是檢查,所以我真的不知道是什麼原因導致了這個錯誤。我會更好地檢查並讓你知道,同時謝謝。 – Jack 2010-11-21 19:10:29

+0

好的,我嘗試了頂層內'types.ml'的完整代碼,它不起作用,給出了相同的錯誤'錯誤:無法安全地評估遞歸定義模塊I的定義......這很愚蠢。雖然你的減少版本的作品,但如果我只是評論那一行還礦工.. – Jack 2010-11-21 19:28:10

+0

這很可能是吉爾斯定義的問題;通過將任何常數值轉換爲單位功能確保模塊安全。 – nlucaroni 2010-11-22 20:11:52