Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 662
codablank1 Avatar asked May 10 '12 21:05

codablank1


People also ask

What is a parameterized module?

Parameterized modules are to modules what functions are to base values. Just like a function returns a new value from the values of its parameters, a parameterized module builds a new module from the modules given as parameters. Parameterized modules are also called functors.

What is a functor in OCaml?

A functor is a module that is parametrized by another module, just like a function is a value which is parametrized by other values, the arguments. It allows one to parametrize a type by a value, which is not possible directly in OCaml without functors.

What are modules OCaml?

ocaml modules allow to package together data structures definitions and functions operating on them. This allow to reduce code size and name confusion, as any identifier can appear in different modules, with different meanings, without any interference.


1 Answers

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
like image 72
Andreas Rossberg Avatar answered Sep 21 '22 12:09

Andreas Rossberg