Ошибка: невозможно безопасно оценить определение рекурсивно определенного модуля - PullRequest
6 голосов
/ 21 ноября 2010

Мне любопытно понять, почему возникает эта ошибка и как лучше всего ее обойти.

У меня есть пара файлов types.ml и types.mli, которые определяют тип варианта value это может быть много разных встроенных типов OCaml (float, int, list, map, set и т. д.).

Так как мне нужно использовать std-lib над этим вариантом, мне нужно было конкретизироватьУстановите модуль через функтор, чтобы можно было использовать наборы типа 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)

AsВы можете видеть, что я должен был определить модуль ValueSet из функтора, чтобы иметь возможность использовать этот тип данных.Проблема возникает, когда я хочу использовать этот модуль внутри объявления I.Чтобы я получил следующую ошибку:

Ошибка: невозможно безопасно оценить определение рекурсивно определенного модуля I

Почему это происходит?Какой хороший способ решить это?И просто чтобы знать, правильный ли мой подход к тому, что я пытаюсь сделать?Кроме того, он работает как задумано (я могу использовать тип ValueSet с моими операциями в других модулях, но я должен прокомментировать соответствующую строку в types.ml, чтобы пройти этап компиляции).

Я пыталсяудалить весь лишний код и уменьшить его до необходимого для расследования этой ошибки .. если это не достаточно, просто спросите:)

РЕДАКТИРОВАТЬ: согласно ссылке OCaml, у нас есть

В настоящее время компилятор требует, чтобы все циклы зависимостей между рекурсивно определенными идентификаторами модуля проходили хотя бы один «безопасный» модуль.Модуль является «безопасным», если все определения значений, которые он содержит, имеют типы функций typexpr1 -> typexpr2.

Это все, что я нашел до сих пор, но я не понимаю точное значение ..

Спасибо заранее

Ответы [ 2 ]

3 голосов
/ 22 ноября 2010

После исправления очевидных ошибок ваш пример компилируется (с 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.empty, а Value небезопасно из-за nil.

Причиной возникновения условия «безопасный модуль» является выбранный метод реализации рекурсивного модуля:

Оценка рекурсивного определения модуля продолжается путем создания начальных значений для задействованных безопасных модулей, связывая все (функциональные) значения до fun _ -> raise Undefined_recursive_module. Определяющий выражения модуля затем оцениваются, и начальные значения для сейфа модули заменяются на вычисленные таким образом значения.

Если вы закомментируете декларацию nil в подписи Value, вы можете оставить определение и декларацию f_empty. Это потому, что Value теперь является безопасным модулем: он содержит только функции. Можно оставить определение nil в реализации: реализация Value не является безопасным модулем, но сама Value (то есть реализация, приведенная к сигнатуре) безопасна.

2 голосов
/ 21 ноября 2010

Я действительно не уверен, какой синтаксис вы используете в подписи, что позволяет 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)
...