Abstract type implementors still forced to submodules?

While learning OCaml, I have been surprised by inconsistency between automatic wrapping of “compilation units” and system of modules subtyping:
Incomplete units

Is it still a thing and have to resolve in creation of nested modules? I.e. is there a way to tell compiler to not generate wrapping module for .ml file? Or a way to alter generated module’s signature?

I guess that by “wrapping module” you mean an interface file (.mli)? In the page you link, the author gives an example of two implementations of a stack data structure that are put in files listStack.ml and customStack.ml and argues that it is not possible to specify a single interface in a file stack.mli shared by both modules.

However, there is a fairly common idiom that achieves precisely this: you need to put the contents of the common signature in a file called, say, stack_intf.ml (note the .ml extension), and then create files listStack.mli and customStack.mli “including” Stack_intf. So you get in the end something that looks like this:

(* stack_intf.ml *)
module type S = sig
  type 'a t
  val empty : 'a t
  ...
end
(* listStack.ml *)
type 'a t = 'a list
let empty = []
...
(* listStack.mli *)
include Stack_intf.S
(* customStack.ml *)
type 'a t = ...
...
(* customStack.mli *)
include Stack_intf.S

This style is famously used by the “Base” (and “Core”) alternative standard libraries of OCaml.

Cheers,
Nicolas

1 Like

This is described in details in the _intf trick.

Thanks for the reply.

Is that described way with “proxy” module type Stack_intf.S equivalent to subtyped module defined at toplevel? I mean equivalent to explicit declaration of module type and module definition:

module  type Stack  = sig
  type 'a t
  val empty : 'a t
end

module ListStack : Stack = struct
  type 'a t = 'a list
  let empty = []
end

It obviously introduces two new modules Stack_intf and Stack_intf.S. Are there other “side effects” with this trick?

Yes.

No.

Cheers,
Nicolas

The _intf trick solves the problem within the language, but sometimes it makes more sense to solve it with your build system, which can simply copy/symlink the .ml file to the name you need. I believe Dune supports this using select.

Example: a.mli with a_windows.ml and a_unix.ml.