Re: Q: Q: Module system and separate compilatioQ:

Xavier Leroy (Xavier.Leroy@inria.fr)
Tue, 26 Mar 1996 17:04:02 +0100 (MET)

From: Xavier Leroy <Xavier.Leroy@inria.fr>
Message-Id: <199603261604.RAA14390@pauillac.inria.fr>
Subject: Re: Q: Q: Module system and separate compilatioQ:
To: Jocelyn.Serot@lasmea.univ-bpclermont.fr (Jocelyn Serot)
Date: Tue, 26 Mar 1996 17:04:02 +0100 (MET)
In-Reply-To: <199603211208.NAA07231@concorde.inria.fr> from "Jocelyn Serot" at Mar 21, 96 01:10:16 pm

> First of all, thanks to W. Lux and C. Boos for their clever answers
> to my last questions about the interaction between the module system
> and the file system. What i had not realized - it seems - is that
> the module language is _implicitely_ spoken within the file system
> and that functor application takes place automatically during the
> linking phase.

This is essentially correct: compilation units (files) are special
cases of structures (module-class objects). However, you can still do explicit
parameterization and explicit functor applications in a context of
separate compilation. That's because all module-class objects,
including functors, structures, and module types, can be components of
structures, hence components of compilation units.

> With the "all in same file, with explicit module declaration" approach , you
> can write:
>
> module type Bar = sig ... end
>
> module type Foo = sig ... end
>
> module Foo1 : Foo = struct ... (* 1st impl *) ... end
>
> module Foo2 : Foo = struct ... (* Another impl *) ... end
>
> module MakeBar(M: Foo) = (struct ... let double = ... Foo.add ... end : Bar)
>
> and then _choose_ to build a Bar implementation using either Foo1
>
> module Bar = MakeBar(Foo1)
>
> or Foo2
>
> module Bar = MakeBar(Foo2)
>
> How can you translate the same approach with the signatures and
> implementations for Foo and Bar being in distinct files (that is:
> foo.mli, foo1.ml, foo2.ml, bar.mli and bar.ml) ?

I would have the following compilation units:

* "foosig" contains the module type Foo
e.g. foosig.ml: module type Foo = sig ... end
and if you wish, foosig.mli with the same contents.

* "foo1" and "foo2" containing the two implementations
foo1.ml: module Foo = struct ... end
foo1.mli: module Foo : Foosig.Foo
foo2.ml: module Foo = struct ... end
foo2.mli: module Foo : Foosig.Foo

* "bar" containing the MakeBar functor and the Bar modtype
bar.ml: module type Bar = ...
module Make(M: Foosig.Foo) = ...
bar.mli: module type Bar = ...
module Make(M: Foosig.Foo): Bar

* "main1" and "main2" that choose between the two implementations
and starts up the whole program
main1.ml: module Bar = Bar.Make(Foo1.Foo) let _ = Bar.doit(); exit 0
main2.ml: module Bar = Bar.Make(Foo2.Foo) let _ = Bar.doit(); exit 0

Then, link-time configuration is achieved by linking either main1 or
main2 with the remainder of the compilation units.

So, it can be done. I'm not saying this is completely satisfactory,
and more support for link-time configuration could be desirable. On
the other hand, I'd hate to add a third language just for controlling
the linker...

- Xavier Leroy