Modules/Namespaces and conviniently adding features to a library

There’s, say, this library that exposes a module M which has a sub-module N.

I’m working on a project that has this special type 'a X.t and a way to create those special values.

I want to use M and M.N in the rest of the project and expose those special values for all the types of M and N. For convenience, it’d be good to have them in the same namespace. So I can use M.foo : M.t as provided by the library and M.bar : M.t X.t as provided by my project. The difficult part becomes M.N.bar : M.N.t X.t.

More concrete example below, with code.

What’s the recommendations for that?
Can I do it without repeating the name of all the exported values in M/M.N?
And more fundamentally: why isn’t module shadowing a thing?


Setup:

(* Toy library *)
module M = struct
  type t = Int of int | Float of float
  let int i = Int i
  let float f = Float f
  module N = struct
    type t = (int * float)
    let cons i f = (i, f)
    let int (i, _) = i
    let float (_, f) = f
  end
end

(* Toy module *)
module X = struct
  type 'a t = ('a * string)
  let name ~name x = (x, name)
end

V1: Partial extension

(* Adding X capability to M *)
module M = struct
  include M
  type named = t X.t
  let name (v: t) : named = X.name ~name:"M" v
end

V2: Complete but incorrect extension (Multiple definition of the module name N. Names must be unique in a given structure or signature.)

(* Adding X capability to M *)
module M = struct
  include M
  type named = t X.t
  let name (v: t) : named = X.name ~name:"M" v
  module N = struct
    include N
    type named = t X.t
    let name (v: t) : named = X.name ~name:"M.N" v
  end
end

V3: Complete but verbose

(* Adding X capability to M *)
module M = struct
  type t = M.t = Int of int | Float of float
  let int = M.int
  let float = M.float
  type named = t X.t
  let name (v: t) : named = X.name ~name:"M" v
  module N = struct
    type t (* = M.N.t *) = (int * float)
    let cons = M.N.cons
    let int = M.N.int
    let float = M.N.float
    type named = t X.t
    let name (v: t) : named = X.name ~name:"M.N" v
  end
end

V4: Alternative (using a separate module/namespace)

module M_X = struct
  type named = M.t X.t
  let name (v: M.t) : named = X.name ~name:"M" v
  module N_X = struct
    type named = M.N.t X.t
    let name (v: M.t) : named = X.name ~name:"M.N" v
  end
end

Ideally, I’d like something like V2: concise within a single namespace. But only V3 (verbose) and V4 (separate namespace) work. Ideas? Suggestions?

My intuition is to design the library using functors.

What about this for V2:

(* Adding X capability to M *)
module M = struct
  include (M : module type of struct include M end with module N := M.N)
  type named = t X.t
  let name (v: t) : named = X.name ~name:"M" v
  module N = struct
    include M.N
    type named = t X.t
    let name (v: t) : named = X.name ~name:"M.N" v
  end
end

It’s worth noting that V2 will be accepted without modification starting with OCaml 4.08.

1 Like

@let-def That works! Thanks!

@trefis good to know…