Combining functors for modules that extend a base module type

I have a module signatures like this:

module type Base_X = sig
  type t
end

module type Base_Y = sig
  type t
end

module type A_sig = sig
  module Some_X : Base_X

  module Some_Y : Base_Y

  module Z : sig
    val f : Some_X.t -> Some_Y.t
  end
end

and two types x and y.

Modules that implement A_sig will define Some_X and Some_Y as extending Base_X and Base_Y in a way that allows instances of x and y to be converted to Some_X.t and from Some_Y.t. For instance, one implementation of Some_X might have type

module type X1_sig = sig
  type t

  val x_to_string : x -> string

  val string_to_t : string -> t
end

For each possible module type like X1_sig there will be a functor X1_func (or function using first-class modules) that takes Some_X1 : X1_sig to (a module containing) a function x -> Some_X1.t, and likewise there will be functors to create functions Some_Y.t -> y.

I would like to define a functor Make that takes a module A : A_sig and appropriate functors X_func and Y_func, and produces a function f : x -> y by chaining X_func, A.Z.f and Y_func. It seems like this isn’t possible to do completely generally, and multiple Make functors are needed for different values of X_func and Y_func. But is there a way to do it more generally than by defining specific versions of Make for each possible pair of X_sig/X_func and Y_sig/Y_func? I would be happy with an approach that handles each X_sig and Y_sig separately, but I can’t work out how to do that (defining a functor/function with type x -> Some_X1.t causes the type constructor Some_X1.t to escape its scope).

You should give a precise example of what you tried. Otherwise, the important details are hidden inside your text and are quite unclear. In particular, it is unclear what is supposed to be the type of your functors. Another worrying point is that your module type A_sig can be rewritten as

module type T = sig type t end
module type A_sig = sig
  module X: T
  module Y: T
  module Z: sig
    val f : X.t -> Y.t
  end
end

Rewriting this module type in such way makes it a little clearer that a module of type A_sig cannot be used without adding more equations to the types.

Trying to follow your description, it sounds like you are trying to do something like:


module type f = sig
  type x
  type y
  module type t
  module F: functor(X: t) -> sig val f:x -> y end
end

module Make
    (A:A_sig)
    (Fx: f with type y := A.X.t)
    (Fy: f with type x := A.Y.t)
    (X: Fx.t)
    (Y: Fy.t):
    sig
      val f: Fx.x -> Fy.y
    end =
struct
  let f x =
    let module FX = Fx.F(X) in
    let module FY = Fy.F(Y) in
    x |> FX.f |> A.Z.f |> FY.f
end

Thanks, I got something working based on that.