I have a very very long file which contains a set of recursive functions. The recursion is necessary and the code is effective as well. But I just want to split the file into 2 or more files to have a better readability. But I don't see how we could split let rec ... and ...
in OCaml...
Does anyone know if OCaml provides any mechanism (e.g., a way to specify interfaces or write makefile
) to do so?
The very very long file may look like:
let rec f1 () =
...
f7 (); (* f7 is called only once by all the functions in the file *)
f2 ();
...
and f2 () =
...
f1 ();
...
and f7 () =
...
f1 ();
...
There is a way: mutually recursive functors. See this great article on separate compilation in OCaml and this inspiring article which provides the main idea.
Here is an example. I've created 4 interface 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 is the interface files' contents:
(* 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 let's 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 command sequence:
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
--------------
B: 4
A: 3
B: 2
A: 1
Just for reference, there is another way, without functors. You have to put functions from the other module as parameter of your functions. Example : we have even
and odd
defined recursively, but we want even
to be in a module A
and odd
to be in a module B
.
First file :
(* A.ml *)
let rec even odd n =
if n=0
then true
else
if n=1
then false
else (odd even) (n-1) ;;
Second file :
(* B.ml *)
let rec odd even n =
if n=1
then true
else
if n=0
then false
else even odd (n-1) ;;
Third file :
(* C.ml *)
let even = A.even B.odd
and odd = B.odd A.even ;;
print_endline (if even 5 then "TRUE" else "FALSE") ;;
print_endline (if odd 5 then "TRUE" else "FALSE") ;;
Compilation (you must use the option -rectypes
) :
ocamlc -rectypes -c A.ml
ocamlc -rectypes -c B.ml
ocamlc -rectypes -c C.ml
ocamlc -rectypes A.cmo B.cmo C.cmo
I'm not sure I would recommand that, but it works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With