Module types for multi-argument functors

Hi all, I have a module function of multiple arguments (simplified here), where the second argument depends on the first.:

module Example = (A : {type t}, B : {let x : A.t}) => { ... }

In reality, the module types for the arguments A and B are much larger, so I want to define some module types for them, but I can’t see how to declare the type of B separately where the module A is in scope.

module type AType = {type t}
module type BType = ???

Currently my solution is to “curry” the modules:

module type AType = {type t}
module Example0 = (A : AType) => {
  module type BType = { A.t }
  module Example1 = (B : BType) => {
    ...
  }
}

But this is a bit annoying because then to fully apply the functors I would have to introduce a bunch of intermediate modules:

module MyExample0 = Example0(MyA)
module MyExample = MyExample0.Example1(MyB)

Is there a better way to do this??

I figured out one workable solution on my own, but I’d still be interested in others’ opinion.

My solution was to define a module types like so:

module type AType = { type t }
module type A_BType = { 
   module A : AType
   module B : { let x : A.t }
}

Then my Module Function is now:

module Example = (A_B : A_BType) => {
  open A_B
  ...
}

applied like so:

module MyExample = Example({ module A = MyA; module B = MyB })
1 Like

@liamoc If I’m not mistaken, what you’re actually looking for is this:

module Example = (A: AType, B: BType with type t = A.t) => {
  ...
}
1 Like

No, that’s not the same. In my case, the module B doesn’t define a type t, but in your case it must.

Well you have to, otherwise how would you define a relation between A and B?

In this case, it seems that B has a value of type A.t, rather than containing a definition of t itself.

The module type for B will need an abstract type in order to have something to substitute, but the substitution can be destructive so that the module doesn’t have to define it:

module type B = {
  type t
  let value: t
}

module M: B with type t := int = {
  let value = 42
}

Note that := is used for the type substitution, instead of =, to specify that the substitution is destructive.

3 Likes

Indeed I was missing glennsl technique, with this you can have:

module type AType = {
  type t 
}

module type BType = {
  type t
  let x: t
}

module Example = (A: AType, B: BType with type t := A.t)  => { 
	let x = B.x
}

module MyExample = Example({type t = int}, {let x = 2}) 

That looks pretty close to what you were trying to achieve I think @liamoc.

1 Like