Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

module

ocaml

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

like image 766
Jack Avatar asked Nov 21 '10 16:11

Jack


2 Answers

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.

like image 152
Gilles 'SO- stop being evil' Avatar answered Sep 27 '22 18:09

Gilles 'SO- stop being evil'


I'm really not sure what kind of syntax you're using in the signature that allows let ... I'm going to assume it was a mistake while reducing the code for us. You also don't need that OrderedType definition, possibly another fiddling error for us, since you don't use it in parameterisation of the Set module.

Aside from that, I have no problem running the following in the toplevel. Since this works pretty directly, I am unsure how you're getting that error.

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)
like image 29
nlucaroni Avatar answered Sep 27 '22 17:09

nlucaroni