Search code examples
interfaceocamlfunctor

Can multiple modules have the same module type? How do I organize them and their interface files?


Currently, I have within the same OCaml file, blah.ml:

module type blah = 
 sig 
  val a : some-type
 end

module type X = 
 sig
  val x : some-type
 end

module Y : X = 
 struct
  let x = some-def
 end

module Z : X = 
 struct
  let x = some-other-def
 end

blah.mli looks like this:

module type blah = 
 sig
  val a
 end

module type X = 
 sig
  val x : some-type
 end

module Y : X

module Z : X

I want X, Y, and Z to be in separate files with separate interfaces. How do I tell in Y.mli and Z.mli that Y and Z have type X?

Any readings for this would also be appreciated. There are a lot of resources talking about modules, interfaces, and functors, but they don't mention interface files for modules that have other modules as types.


Solution

  • You can create x.ml containing the sig, y.ml containing the module, and z.ml containing that module. You don't need to do anything special to tell the compiler that Y : X and Z : X. The compiler infers the module type automatically from the fact that the module conforms to the type i.e. it implements every binding that the module type needs. If Y or Z don't conform, the type error will be shown at the point of use.

    If you want to restrict the module type at the point of definition that's also doable, by giving each module an interface file and includeing the required signature there. For example:

    (* x.ml *)
    module type S = sig
      val x : some-type
    end
    
    (* y.mli *)
    include X.S
    
    (* y.ml *)
    let x = some-def
    

    ...and so on.

    However this often becomes too restrictive as the signature hides too much detail about the types. So in reality you may actually need to add type equality sharing constraints to avoid compile errors where you want to expose more type info. E.g.:

    (* y.mli *)
    include X.S with type t = int
    

    Often it is easier to not have the explicit interface file at all unless you really need to make some parts of the module private.