How defining a module signature whose module implementation is parameterized by a functor

functional-programming, functor, module, ocaml

Solution

The natural way to do this is to seal the module at the definition site, not the use site. Then you just need to express the type sharing once:

module M (F : sig type id type data end) :
  S with type idtype = F.id and datatype = F.data
  = struct ... end

If your functor parameter is more complex then you can also just share an entire module instead of individual types. For example:

module type TYPES = sig type id type data (* ...and more... *) end

module type S = 
sig
  module Types : TYPES
  type component
  val create : Types.id -> Types.data -> component
  val get_comp_data : component -> Types.data
  val get_comp_id : component -> Types.id
end

module M (F : TYPES) : S with module Types = F
  = struct ... end

Or you can even parameterise the signature itself, by nesting it into another functor:

module type TYPES = sig type id type data (* ...and more... *) end

module S (F : TYPES) =
struct
  module type S =
  sig
    type component
    val create : F.id -> F.data -> component
    val get_comp_data : component -> F.data
    val get_comp_id : component -> F.id
  end
end

module M (F : TYPES) : S(F).S
  = struct ... end

Problem

Let's say I have a module `M` parameterized by a module `F`: ``` module M (F : sig type id type data end) = struct type idtype = F.id type datatype = F.data type component = { id : idtype; data : datatype } let create id data = { id; data } let get_comp_data comp = comp.data let get_comp_id comp = comp.id end ``` so I use it like this : ``` module F1 = struct type id = int type data = float end module MF1 = M(F1) let comp = MF1.create 2 5.0 let id = MF1.get_comp_id comp ``` Now, if I want `M` to match signature `S` : ``` module type S = sig type idtype type datatype type component val create : idtype -> datatype -> component val get_comp_data : component -> datatype val get_comp_id : component -> idtype end module F1 = struct type id = int type data = float end module MF1 = (M(F1) : S) let comp = MF1.create 2 5.0 let id = MF1.get_comp_id comp ``` what bothers me here is, in order to define `get_comp_data` and `get_comp_id`, I need to specify `idtype` and `datatype` in module `S`; now just imagine I have other records types in `M` with their own types, I'll have a dozen types to specify in `S`? Is there a simpler way to avoid that?

Original source