After `module M = Map.Make(O)`, what should go in the signature?

(Obligatory disclaimer: I am new to OCaml!)

In a module, I have

module IdMap = Map.Make(Int)

Since the module exports a function which returns an IdMap, I need to declare IdMap in the signature of the module. I think this is what was discussed in 8546, which seemed to suggest:

module IdMap : module type of Map.Make(Int)

My question is: is this correct, and could you point me to the section of the manual that explains this? (I am assuming it’s common to want to declare the outputs of functors in a signature; but if it’s idiomatic to do something else I would be very happy to know that also.)

Many thanks!

Your module declaration is correct in isolation, but it’s hard for me to tell if this is what you want to do without more information. Here is an alternative :

module IdMap : Map.S with type key = int (* or type key := int if you want a destructive replacement *)

I tend to use this one but I have to admit I could not explain why.
By the way, by returning an IdMap do you mean returning a first class module of the same type as Map.Make(Int) ? In this case you probably want to declare a module type instead of a module :

module type IDMAP = Map.S with type key = int
val build_map: foo -> (module IDMAP)

Ah, sorry: I was unclear. I meant “returning a value whose type is 'a IntMap.t”. (I think that’s how one writes it, anyway. I’m certainly returning a map from ints to things.)

I don’t know what destructive replacement is (which I think tells you what my level is!), however I can look up your alternative in the docs, I suspect, and read what it means. I think this will help me!

Many thanks indeed.

I use it because module type of introduces an extra indirection for someone looking up the type of your functor, which can make documentation browsing tiresome; I try to have an explicit module type for all functor results, and glue things using with constraints. It is kind of verbose but it feels more readable to me.

2 Likes

A couple other notes:

  • With the Map.S with type key = int version, you hide the fact that 'a IdMap.t = 'a Map.Make(Int).t. This may or may not be desirable. So for example:
module M_impl = struct
  module IdMap = Map.Make (Int)
end

module M : sig
  module IdMap : Map.S with type key = int
end =
  M_impl

module M' : sig
  module IdMap : module type of Map.Make (Int)
end =
  M_impl

module Other_map = Map.Make (Int)

(*   let l = [ M.IdMap.empty; Other_map.empty ]
                              ^^^^^^^^^^^^^^^
Error: This expression has type 'a Other_map.t
       but an expression was expected of type 'b M.IdMap.t
*)
let l = [ M.IdMap.empty; Other_map.empty ]

(* This works fine: *)
let l2 = [ M'.IdMap.empty; Other_map.empty ]
  • If you just want to expose particular IdMap.t values (or functions returning or consuming those values, etc.) from your module, you don’t have to expose the IdMap module: it’s perfectly legal to write they type 'a Map.Make(Int).t, as in val id_map : string list -> string Map.Make(Int).t.
1 Like

Thanks both, this is very helpful indeed.

It’s clear I need to re-read the manual on modules again; but I have enough to make things compile for now.