There is a way: mutually recursive functors. See this excellent article on standalone compilation in OCaml and this inspiring article for a basic idea.
Here is an example. I created 4 interfaces mli files:
$ ls *.mli Make_moduleA.mli Make_moduleB.mli ModuleA.mli ModuleB.mli
and 3 implementation files:
$ ls *.ml Make_moduleA.ml Make_moduleB.ml main.ml
Here are the contents of the interface files:
(* ModuleA.mli *) module type ModuleA = sig val fa : int -> unit end (* ModuleB.mli *) module type ModuleB = sig val fb : int -> unit end (* Make_moduleA.mli *) open ModuleA open ModuleB module type Make_moduleA_sig = functor (Mb : ModuleB) -> sig val fa : int -> unit end module Make_moduleA : Make_moduleA_sig (* Make_moduleB.mli *) open ModuleA open ModuleB module type Make_moduleB_sig = functor (Ma : ModuleA) -> sig val fb : int -> unit end module Make_moduleB : Make_moduleB_sig
And mutually recursive functors:
(* Make_moduleA.ml *) open ModuleA open ModuleB module type Make_moduleA_sig = functor (Mb : ModuleB) -> sig val fa : int -> unit end module Make_moduleA_impl = functor (Mb : ModuleB) -> struct let rec fa (n : int) = if n > 0 then (Printf.printf "A: %d\n" n; Mb.fb (n - 1)) end module Make_moduleA = (Make_moduleA_impl : Make_moduleA_sig) (* Make_moduleB.ml *) open ModuleA open ModuleB module type Make_moduleB_sig = functor (Ma : ModuleA) -> sig val fb : int -> unit end module Make_moduleB_impl = functor (Ma : ModuleA) -> struct let rec fb (n : int) = if n > 0 then (Printf.printf "B: %d\n" n; Ma.fa (n - 1)) end module Make_moduleB = (Make_moduleB_impl : Make_moduleB_sig)
And now combine the modules:
(* main.ml *) open ModuleA open ModuleB open Make_moduleA open Make_moduleB module rec MAimpl : ModuleA = Make_moduleA(MBimpl) and MBimpl : ModuleB = Make_moduleB(MAimpl) let _ = (* just a small test *) MAimpl.fa 4; print_endline "--------------"; MBimpl.fb 4
Build a sequence of commands:
ocamlc -c ModuleA.mli ocamlc -c ModuleB.mli ocamlc -c Make_moduleA.mli ocamlc -c Make_moduleB.mli ocamlc -c Make_moduleA.ml ocamlc -c Make_moduleB.ml ocamlc -c main.ml ocamlc Make_moduleA.cmo Make_moduleB.cmo main.cmo
Test results:
$ build.sh && ./a.out A: 4 B: 3 A: 2 B: 1
source share