I distinguish three organisation levels while programming: the library level, the component level and the application level. A library defines functions to solve a range of related problems or preform related operations in tools and no policies manner. An application lets several libraries interact and defines policies for diagnostic, error handling, internationalisation,… A component sits between the library and the application, it handles low-level errors and produce diagnostics, it might old global state and is aware of other components.
Now I have a library implemented as a functor parametrised by a module holding a constant n and values associated to different values of n have incompatible types—which is the purpose of implementing the library as a functor. I call such a configuration a hard configuration, because it must somehow be hard-coded in the program.
Now I am facing the following problem: In this setting, I want to allow the user of the program to choose the value of n for the duration of the program. Since the library is a functor, one feels forced to write the component interfacing the library to the program as a functor. But I want the choosed value of n to be a property of the component, not of the application, and I want component to be regular modules (no functor allowed).
How can I wrap a parametric library (implemented as a functor) in a non-parametric component, so that parameters can be chosen when the application starts? I insist on being able to express the application logic at the highest level in terms of non parametric modules, and want to avoid russian-dolls-alike parametrised modules at this level.
This sounds exactly like the intended use-case of first-class modules:
A typical use of first-class modules is to select at run-time among several implementations of a signature.
http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec230
Another thought would be to implement component config parameters as mutable cells, something like this:
module Component :
sig
val init : ?foo:int -> ?bar:float -> ?baz:string -> unit -> unit
val foo : unit -> int
val bar : unit -> float
val baz : unit -> string
end = struct
module Default =
struct
let foo = 0
let bar = 0.
let baz = ""
end
let store_foo = ref Default.foo
let store_bar = ref Default.bar
let store_baz = ref Default.baz
let init ?(foo=Default.foo) ?(bar=Default.bar) ?(baz=Default.baz) () =
( store_foo := foo
; store_bar := bar
; store_baz := baz
)
let foo () = !store_foo
let bar () = !store_bar
let baz () = !store_baz
end