How to create a "flat" library of multiple ml files?

I want to create a library (for instance test.cma) of a small project that consists of several files: types.ml, parse.ml, and utils.ml. I would like the namespace to be flat: I want to have access to the types and functions of all three files directly under Test (i.e,. Test.parse : string -> ast and not Test.Parse.parse : string -> Types.ast). Is it possible to create such a library, or do I need to write a master file that collects everything?

2 Likes

You can write a test.ml file as follows:

include Types
include Utils
include Parse

Best wishes,
Nicolás

1 Like

Thank you for the suggestion. It seems like the simplest solution indeed.

I have a followup question: how can I do the same thing in the mli? I tried include but it does not seem to work (I have types.cmi, but if I do include Types I get an error).

You need to use include module type of Types.

Best wishes,
Nicolás

Thanks a lot, that works great.

So our solution was something like this (we decided to put Utils as a submodule):

mylib.mli:

include module type of Types
module Utils : sig
  include module type of Utils
end

mylib.ml:

include Types
module Utils = struct
  include Utils
end

compiled as

ocamlc -c *.ml
ocamlc -a -o mylib.cma types.cmo utils.cmo mylib.cmo

I wonder if there is a simpler solution (I tried using pack but I cannot achieve the same result). And if there are pointers to workflows to create libraries in OCaml, I’d love to read them.

Thanks again!

Looks OK to me. You can shorten your file by writing module Utils = Utils in both the implementation and the interface.

Best wishes,
Nicolás

1 Like

It is worth noticing that module Utils = Utils is not only shorter but gives more informations to the type checker (for instance, if F is an applicative functor introducing an abstract type t, F(Utils).t and F(Types.Utils).t will be equal if Utils and Types.Utils are known to be aliases).

Anyway, you may shorten your code:

into that (which is, as far as I know, strictly equivalent):

module Utils : module type of Utils

and these two forms are weaker than:

module Utils : module type of struct include Utils end

(weaker in the sense that if Utils introduces an abstract type t, the two types are unrelated in the first version, whereas they are equal in the second one), and this form is in turn weaker than

module Utils = Utils

where we get equalities between abstract types under functor application, as I mentioned in the beginning.

1 Like

Wouldn’t it be simpler in this case to just remove test.mli?

This was enlightening, thanks a lot!

We don’t want to expose everything from the submodule, so we prefer to have a mli.