I’ve stumbled into a weird situation recently when using first-class modules. For context, I have to functors with the same type, and I would like to choose one dynamically at run time:
module type BAR = sig type 'a t end
module type FOO = sig module Bar : BAR end
(** 2 functors which look a bit like this *)
module F1(B : BAR) : FOO with module Bar = B = _
module F2(B : BAR) : FOO with module Bar = B = _
The problem comes when trying to choose which functor to us while keeping the information that the resulting FOO
module verifies module Bar = B
.
Essentially, I would like to be able to do something like this:
let f cond (module B : BAR) =
let module G = (val
if cond then (module F1(B)) else (module F2(B))
: FOO with module Bar = B) in
() (* G is used here *)
However, this gets rejected with the error message invalid package type: only 'with type t =' constraints are supported
. Similarly, the constraint directly on the type also fails with a similar error message:
let f' cond (module B : BAR) =
let module G = (val
if cond then (module F1(B)) else (module F2(B))
: FOO with type 'a Bar.t = 'a B.t) in
() (* G used here *)
If the argument module B
is statically known, then I can do something like this:
module B : BAR = _
module type FOO_WITH_B = FOO with module Bar = B
let f cond =
let module G = (val
if cond then (module F1(B)) else (module F2(B))
: FOO_WITH_B) in
() (* do something with G here *)
Which is a bit ugly but somewhat manageable. However, in my case, the B
module is also chosen dynamically… This lead me to the following ugly hack, in which I use a second functor just to generate my signature:
module GenType(B : BAR) = struct
module type T = FOO with module Bar = B
end
let f cond (module B : BAR) =
let module G = (val
if cond then (module F1(B)) else (module F2(B))
: GenType(B).T) in
() (* G used here*)
Is there any cleaner way to do this?
Also is there any rationale behind the restriction of constraints on packaged module types , or is it just something that wasn’t implemented?