Nested functors: Compiler can't determine type equality

After getting used to plan to instantiate functors outside other functors, I get along well in practice now. The module type of syntax (that I recently discovered and shared in my post above) helps too.

For my convenience, however, I ended up with something like this (work still in progress):

module Make
    (Num : Number.S)
    (Vec : module type of Vector.Make (Num))
    (VectorSet : module type of Set.Make (Vec))
    (* ... *) : sig
  (* ... *)
end

module MakeWithDeps (Num : Number.S) : sig
  module Num : module type of Num
  module Vec : module type of Vector.Make (Num)
  module VectorSet : module type of Set.Make (Vec)
  (* ... *)
  module M : module type of Make (Num) (Vec) (VectorSet) (* ... *)
end

Edit: Actually I need to use this slightly more complex syntax to ensure that abstract types remain compatible:

module MakeWithDeps (Num : Number.S) : sig
  module Num : module type of struct
    include Num
  end
  module Vec : module type of struct
    include Vector.Make (Num)
  end
  module VectorSet : module type of struct
    include Set.Make (Vec)
  end
  (* ... *)
  module M : module type of Make (Num) (Vec) (VectorSet) (* ... *)

See the first example code in the OCaml manual on Recovering the type of a module in that matter.

End of edit.


This allows me to create all required modules conveniently (using MakeWithDeps, that just requires the number module), but still allows a user to provide their own vectors, vector sets, etc. to Make.

It’s something that works, but I still feel like it is suboptimal regarding the level of abstraction that I would like to express, ideally.

What feels odd is: I abstract over concrete modules (with module type of), to then later fit in concrete instantiations again:

module MakeWithDeps (Num : Number.S) = struct
  module Num = Num
  module Vec = Vector.Make (Num)
  module VectorSet = Set.Make (Vec)
  (* ... *)
  module M = Make (Num) (Vec) (VectorSet) (*...*)
end

Is there a (tracking) issue that I could follow? Or a keyword I could search for on the issue tracker, so I get updated on any changes? Or any roadmap? Or is this just a rough not-written-down plan at this stage?

I found “Sub-module identities inside an applicative functor are actually generative, leading to invalid signatures · Issue #13173 · ocaml/ocaml · GitHub”, but that seems to be be different issue.

Do other people have struggles with nested functors or is there some reading material on the issue? I attempted some AI query :see_no_evil_monkey:, and was referred to “builder modules”, like MakeWithDeps above, but trying to find concrete examples wasn’t fruitful. So is this really something used in practice? (See also @n4323’s post above, asking if there is some general guide to functors which covers these more complex cases.)