Hi,
I am working on a project where I have a bunch of mutually recursive modules. I am not very happy for one of the modules (let’s call it A
) to be recursive with all the other ones, as logically it makes more sense to have it separate, and have my big mutually recursive modules definition depend on that one module. However, this module A
is still inductive with one of the modules (call it B
) of the mutually recursive modules.
So I would like to make A
into a functor, taking a module with the same signature as B
. So far so good, but my issue here is that the signature of B
is recursive. So ideally I would like to write something like
module rec A :
functor
(B :
sig
type t
val to_a : t -> A(B).t
end
)
-> sig
type t
val to_b : t -> B.t
end
=
functor
(B :
sig
type t
val to_a : t -> A(B).t
end
)
-> struct
type t = int
let to_b = assert false
end
However, this doesn’t work, for the following reason
| val to_a : t -> A(B).t
^^^^^^
Error: Unbound module B
Indeed, there is no way to indicate that the module signature B
. After some googling, I found out that this is indeed one of the limitations of the module system of OCaml. As a workaround, I tried encapsulating the signature into a wrapper module for B
’s signature, that I defined mutually recursively with A
as follows:
module rec A :
functor (B : B_wrapper.S) -> sig
type t
val to_b : t -> B.t
end
=
functor (B : B_wrapper.S) -> struct
type t = int
let to_b = assert false
end
and B_wrapper :
sig
module type S = sig
type t
val to_a : t -> A(S).t
end
end =
struct
module type S = sig
type t
val to_a : t -> A(S).t
end
end
This still doesn’t compile, with the following error message
2 | functor (B : B_wrapper.S) -> sig
^^^^^^^^^^^
Error: Illegal recursive module reference
I understand that the recursive schemes are guarded to prevent inconsistent schemes, but in my case I am pretty sure that what I am doing is in fact consistent. Indeed, I have a version of this working perfectly well when all the modules are mutually recursive. Is there a standard way to achieve what I am trying to do, or a workaround for that kind of scenario?