как делает работу соединения, на которую ссылаются, в OCaml?
Пример, давайте предположим, что у меня есть 3 модуля, объявленные как
A.ml
B.ml
C.ml
из которых
A
потребности B
и C
B
потребности A
Как я должен продолжить двигаться в компиляции?
Так как порядок является соответствующим использованием ocamlc
или ocamlopt
как я могу зафиксировать перекрестную ссылку между B
и A
?
Я пытаюсь сначала скомпилировать их всех в .cmo
с ocamlc -c
и затем соедините, все они, но без успеха начиная со свопинга аргументов просто переместят проблему от модуля до другого.
Определенная ошибка:
Ошибка: Ошибка при соединении A.cmo: Ссылка на неопределенный глобальный 'B'
(или наоборот, если я подкачиваю порядок args),
Я думаю, что это - легкий вопрос, но я не могу решить его..заранее спасибо
Вы должны объединить модули в один файл и сделать их рекурсивными . Я не верю, что есть способ сделать это в процессе компиляции двух отдельных файлов.
module rec A :
sig
val f : int -> int
val g : int -> int
end =
struct
let f x = (B.g x) + 1
let g x = x + 1
end
and B :
sig
val f : int -> int
val g : int -> int
end =
struct
let f x = (A.g x) + 1
let g x = x + 1
end
РЕДАКТИРОВАТЬ: Судя по вашему комментарию, я предполагаю, что у вас есть определение типа парсера и функции, которые обрабатывают / работают с типом в том же файле. Я согласен с вами, в этом есть смысл. Но, как вы уже испытали, если этот файл должен не только работать с типом, но и вызывать синтаксический анализатор для создания данных, как синтаксический анализатор собирается его построить? Мое решение заключалось в том, чтобы выделить тип в отдельный модуль и открыть этот модуль в модуле, выполняющем операции.
Таким образом, вы разбиваете A
на ( A
и A '
), где A'
содержит тип, созданный B
, и используется в A
. Ваши зависимости становятся:
A
требует A '
и B
и C
B
требует A'
Для Например, у меня есть синтаксический анализатор файлов конфигурации, который я использую для запуска любого написанного мной приложения.
ConfType - содержит тип t Conf - вызывает парсер и содержит вспомогательные функции для типа ConfType.t ConfParser - для ocamlyacc ConfLexer - для ocamllex
Альтернативой всему этому является использование полиморфных вариантов. Таким образом вы удаляете зависимости, поскольку они определены специально. Конечно, тип, созданный синтаксическим анализатором, может отличаться от типа в Conf, и компилятор не сможет помочь вам с ошибкой.
Язык Ocaml поддерживает рекурсию между модулями, но компилятор не поддерживает рекурсию между компиляциями. единицы. Таким образом, у вас не может быть A.ml
, требующего B.ml
, и B.ml
, требующего A.ml
. Рефакторинг для удаления рекурсии лучше всего, если вы можете сделать это легко, но давайте предположим, что вы не можете.
Одно из решений, как объяснил nlucaroni, состоит в том, чтобы собрать оба модуля в один файл и использовать module rec
. Другое решение иногда состоит в том, чтобы превратить один модуль в функтор, например, превратить A
в функтор F
, который принимает аргумент с подписью B
, и скомпилировать сначала файл, определяющий F
, затем B
, затем файл, который просто определяет модуль A = F (B)
.
Ocamlyacc действительно все усложняет, но вы можете обмануть его! Вы можете написать module A = functor (...) -> struct
в заголовке .mly
и соответствующий end
в нижнем колонтитуле. Однако вам придется переписать сгенерированный .mli
, чтобы добавить модуль A: functor (...) -> sig
и end
как часть вашей сборки процесс.(Я знаю, что делал это раньше, чтобы решить ту же проблему, что и у вас, но я не помню, где, поэтому не могу привести пример из реальной жизни.)
Еще одна возможность, которую стоит изучить, - это переход с Ocamlyacc to Menhir , который представляет собой замену Ocamlyacc (практически не требует переноса, поскольку синтаксис такой же) с некоторыми полезными функциями, которые могут вам помочь, например, поддержка параметризованных модулей синтаксического анализатора (то есть функторов).