modules local to functions. (again)

Christian Boos (boos@gr6.u-strasbg.fr)
Fri, 16 Feb 96 11:12:19 +0100

Date: Fri, 16 Feb 96 11:12:19 +0100
From: boos@gr6.u-strasbg.fr (Christian Boos)
Message-Id: <9602161012.AA17808@gr6.u-strasbg.fr>
To: caml-list@pauillac.inria.fr, boos@pauillac.inria.fr
Subject: modules local to functions. (again)
In-Reply-To: <199601101527.QAA22663@pauillac.inria.fr>
<199601101527.QAA22663@pauillac.inria.fr>

Hello,

I want to restart the discussion about what the subject says (module
local to functions), because Xavier Leroy's conclusion (which I recall
below) was not satisfactory for me.

Xavier Leroy writes:
> ...
>
> I still need to be convinced of the usefulness of local functor
> applications (Andrew Conway's example was too sketchy to illuminate me),
> but it can definitely be done.
>

Perhaps the following example will be more convincing !

Let suppose that you fully play the game of modular programming,
that is, designing your algorithms in terms of independant parts,
basic algorithms (direct modules instantiations) and parametrable
algorithms (functors).
In several situations, it may be usefull to instantiate functors
in many different, ways, by using various parameter modules.
Those different ways will produces different results, each equally
usefull in a particular, end-user-defined, context.
For instance, you've got 3 modules of type T (A, B, C), four functors
of type T -> T' (F1, F2, F3, F4) and another functor G of type T' ->
T'', which produce the module you're interested in.
Even in this small example, you've got 12 different ways to produce
a T'' module.
How to write a stand-alone program which allows the user to select
the kind of T'' functionnality he wants ?

You may write:

(* prog1.ml *)

let base = ref "a" and fonct = ref 1

module G_A1 = G(F1(A))
module G_A2 = G(F2(A))
... (* nine other module instantiations *)
module G_C4 = G(F4(C))

let main =
Arg.parse [ "-base", String ((:=) base); "-f", Int ((:=) fonct)]
usage;
let f =
match (!base, !fonct) with
("a", 1) -> G_A1.f
| ("a", 2) -> G_A2.f
| ... (* nine other selections *)
| ("c", 4) -> G_C4.f
| _ -> failwith "no such base modules or functors"
in
(* hopefully you don't need anything else from G ... *)
f "youpi" ();
exit 0

You may also run in things such like automaticaly generate csl code
wich instantiates the module you need, compile it, and executing it (or
even dynamically load it); but that's hacking, not modular functionnal
programming.
Neither solution is satisfactory.

Let's cite X.L. again:

Xavier Leroy writes:
> ...
>
> So, there is no deep reason why modules could not be defined locally
> to a (core-language) expression. Standard ML allows this (the "let
> structure" and "local" constructs). That was left out of Caml Special
> Light for simplicity. In particular, not having module expressions
> inside core-language expressions avoids mutual recursion between the
> module language and the core language; this makes both the theory and
> the implementation simpler.
> ...

... and make my two cents proposal:

* allow local module instantiation, along with local "open"s

* extend the langage such that with each expression <expr> is
associated a module type.
** existing <expr> construct will be associated the void
module type (that is, the most general one: sig end).

** add the following constructs:

'module <ModName>'
which is a module constant, having 'unit' for regular
(value) type, and it's own type module.

'module <ModName> = <expr> in <expr>'
which is a module instantiation, also having 'unit' for
regular type.

Now, you have to perform two kinds of type-checking for expressions:
the usual one, plus the module type checking, but the later will be
trivial in almost all cases (matching 'sig end' against 'sig end').
I'm not sure whether this proposal is realistic or not, but at
least, it provides an elegant way to rewrite the previous example
program:

(* prog2.ml *)
let base = ref "a" and fonct = ref 1
let main =
Arg.parse [ "-base", String ((:=) base); "-f", Int ((:=) fonct)]
usage;
module Base =
match !base with
"a"|"A" -> module A
| "b"|"B" -> module B
| "c"|"C" -> module C
| _ -> failwith "unknown base module"
in
module F =
match !fonct with
1 -> module F1
| 2 -> module F2
| 3 -> module F3
| 4 -> module F4
| _ -> failwith "unimplemented method"
in
module MyG = G(F(Base) in
MyG.f "youpi again"
(MyG.g "now we can use everything from G without dispatching again !");
exit 0

[French translation: (:-) ]
Pour une fois que je fais l'effort de tout ecrire directement en
anglais, vous pouvez bien faire l'effort de me lire en anglais, non ?

-- Christian Boos