Version française
Home     About     Download     Resources     Contact us    
Browse thread
Re: One more question about the module system
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Xavier Leroy <Xavier.Leroy@i...>
Subject: Re: One more question about the module system

> I have a program implemented in several files (say m.ml, m1.ml and m2.ml).
> This program depends on a module L and the dependency is expressed
> using the .mli/open mechanism. So I have:
> 
> 	(* m.ml *)
> 	open L
> 	open M1
> 	...
> 
> 	(* m1.ml *)
> 	open L
> 	open M2
> 	...
> 
> 	(* m2.ml *)
> 	open L
> 	...
> 
> I want to transform this program in order to have a functor of argument L
> (i.e. I want to use twice my program with two different module L's). So
> I want to be able to write (in a main.ml):
> 
> 	module ML1 = M(L1)
> 	module ML2 = M(L2)
> 	...
> 
>  where M is the functor.
> 
> I use the following idea: because my different (implicit) modules
> depend on L, I have to translate them into (explicit) functors. Then, I
> apply the functors for m1.ml and m2.ml in the main module defined in m.ml.
> The m1.ml module can no longer include an "open M2" since M2 is a functor:
> m1.ml have to speak about M2 through the instantiation of M2 done in M.

No, but M1.Make can take L as a parameter, apply M2.Make to it, bind
the result with a "module" binding, and "open" the result. E.g.

(* m1.ml *)
        module Make(L: MTYPE_L) = struct
          module M2L = M2.Make(L)
          open L
          open M2L
          ...
        end
(* m.ml *)
        module Make(L: MTYPE_L) = struct
          module M1L = M1.Make(L)
          open L
          open M1L
          ...
        end

However, it's often more flexible to avoid applying functors inside
functors. Instead, just pass extra parameters to your functors:

(* m1.ml *)
        module Make(L: MTYPE_L)(Some_M2: MTYPE_M2) = struct
          open L
          open Some_M2
          ...
        end
(* m.ml *)
        module Make(L: MTYPE_L)(Some_M1: MTYPE_M1) = struct
          open L
          open Some_M1
          ...
        end

(You will probably need to express sharing constraints between type
components of the parameters. Express them with "with type" or
"with module", e.g.
        module Make(L: MTYPE_L)(Some_M2: MTYPE_M2 with type t = L.t) ...
)

Then do all the functor applications in a "main" file:

        module L = L1 (* or L2 *)
        module M2 = M2.Make(L)
        module M1 = M1.Make(L)(M2)
        module M = M.Make(L)(M1)

The advantage of this approach is that you can also provide
"hand-made" M1 or M2 modules (not produced by application of M1.Make
or M2.Make), so you get more flexibility. The disadvantage is
increased verbosity and number of declarations needed.

- Xavier Leroy