Is it possible to split a set of recursive functions into 2 files in OCaml?

I have a very long file containing a set of recursive functions. Recursion is necessary, and the code is also efficient. But I just want to split the file into 2 or more files in order to have better readability. But I don’t see how we could split let rec ... and ... into OCaml ...

Does anyone know if OCaml supports any mechanism (for example, a way to specify interfaces or write makefile )?

A very long file might look like this:

 let rec f1 () = ... f7 (); (* f7 is called only once by all the functions in the file *) f2 (); ... and f2 () = ... f1 (); ... and f7 () = ... f1 (); ... 
+5
source share
2 answers

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 -------------- B: 4 A: 3 B: 2 A: 1 
+3
source

Just for reference, there is another way, without functors. You must put functions from another module as a parameter of your functions. Example: we have even and odd defined recursively, but we want even be in module A and odd to be in 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 should use the -rectypes option):

 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 recommend this, but it works.

+2
source

Source: https://habr.com/ru/post/1247642/


All Articles