Modules and generic type signature

Hi everyone,

I am new to Ocaml and functional programming. I have been working on a toy project where I am trying to get comfortable with the Ocaml module system. Btw, I am using the jane street base library.

Basically, I have a module of type:

module type CONFIG = sig
  type 'a t

  val tCol : 'a t -> 'a

  val fCol : 'a t -> 'a list

  val makeCol : 'a -> 'a list -> 'a t
end

And a Functor that maps over the Comparator.S module.

module MakeConfig (C : Comparator.S) = struct
  type 'a t =
    {
      tcol : 'a;
      fcol : ('a, C.comparator_witness) Set.t
    }

  let tCol { tcol = tocolumn; fcol = _ } = tocolumn

  let fCol { tcol = _; fcol = fromcolumn } = Set.to_list fromcolumn

  let makeCol t_col f_col =
    {
      tcol = t_col;
      fcol = Set.of_list (module C) f_col
    }
end

This allows me to make modules with multiple implementations of the Comparator e.g.

module StringConfig = MakeConfig (String)

How do I go about about adding the module type signature? I am trying to do something like this,

module StringConfig : CONFIG with type 'a t = string t = MakeConfig (String)

Is this possible in Ocaml with the record type? Or if there is a better way to design the type.

Thanks,

There is two issue, first your module type is not adapted to your functor result.
Indeed makeCol has type C.t -> C.t list > C.t t. Thus, you might have intended to write:

module type CONFIG = sig
  type elt
  type 'a t
  val tCol: 'a t -> 'a
  val fCol: 'a t -> 'a list
  val makeCol: elt -> elt list -> elt t
end

Then if you want to hide the implementation of 'a t in StringConfig you can do it with

module StringConfig
  : CONFIG with type elt := string
  = MakeConfig(String)

But then, the only to produce a value of type 'a t is through makeCol which only produces values of type string t. Therefore, it might make sense to remove the type parameter of t by making it specialized for elt t . Or, you can keep the implementation visible with:

module StringConfig
  : CONFIG with type elt := string
      and type 'a t = MakeConfig(String).t
  = MakeConfig(String)

But then, this kind of decision is generally make at the functor level.
So you would either define MakeConfig as

module MakeConfig(C : Comparator.S) : CONFIG with type elt := C.t = ...

Or if you want to keep the type definition visible, you need to move it to the signature.
You can then either use add the type definition first and then include the CONFIG module type after removing the types already defined

module MakeConfig(C : Comparator.S) : sig
    type 'a t =
    {
      tcol : 'a;
      fcol : ('a, C.comparator_witness) Set.t
    }
   include CONFIG with type elt := C.t and type 'a t:= 'a t
  end = ...

or you can update the module type itself:

module type CONFIG = sig
  type elt
  type witness
  type 'a t =
    {
      tcol : 'a;
      fcol : ('a, witness) Set.t
    }
  val tCol: 'a t -> 'a
  val fCol: 'a t -> 'a list
  val makeCol: elt -> elt list -> elt t
end

module MakeConfig(C : Comparator.S) 
  : CONFIG with type elt := C.t and type witness:= C.comparator_witness
  = ...

Hi @octachron,

Thank you very much for the detailed explanation.

I ended up with the following module type signature:

module type CONFIG = sig
  type elt
  type 'a t

  val tCol : 'a t -> 'a

  val fCol : 'a t -> 'a list

  val makeCol : elt -> elt list -> elt t
end

And the functor as the following:

module MakeConfig (C : Comparator.S)
  : CONFIG with type elt := C.t = ...

My goal was to provide the client with a function which, when supplied with a comparator module will produce the CONFIG module. The final function looks like the following:

val prepareConfig :
  (module Core.Comparator.S with type t = 'a) ->
  (module CONFIG with type elt = 'a) = <fun>

let prepareConfig
    (type a)
    (module C : Comparator.S with type t = a) =
  (module struct
    type elt = a
    include MakeConfig (C)
  end : CONFIG with type elt = a)

Thanks for the help.