How can I generate .mli files from .ml files?

Right now, I write my .ml files, open utop and use #show to see the inferred interface and copy-paste that into a .mli file, which I then edit to remove private interfaces. Is there a more automated way to show the generated interface?

I’m sure this has been asked a thousand times, but I couldn’t find it. Thanks.

2 Likes

There is the option -i, e.g. ocamlc -i file.ml.

I have a follow-up question: I more or less managed to do this with dune by going to the _build directory with ocamlc -i -I . file.ml. Is there a better way to generate the .mli with dune?

3 Likes

I was able to use merlin at one point to do this, might work for you: https://gist.github.com/yawaramin/b86557ae81cbd019fcb9e071abe594de

I didn’t use ocamlc -i ... because it needs to be told about all dependency modules explicitly, even third-party ones. Merlin doesn’t.

As @gadmm says, it’s what the -i option is intended for. However in practice, the actual command line can be very complicated due to various dependencies.

What I’ve been using is the cmi file produced by building the ml file normally, and then generate an mli file from that. It’s a simple translation from a binary format to a text format and it doesn’t need to know about dependencies. The tool for this is cmitomli, available in opam. It goes like this:

$ opam install cmitomli
$ find _build -name file.cmi
_build/path/to/file.cmi
$ cmitomli _build/path/to/file.cmi > file.mli

Note for the curious: the implementation of cmitomli is a simple wrapper around functions from the compiler libs.

6 Likes

I use the reason-vscode plugin to add OCaml support to vscode: https://github.com/jaredly/reason-language-server

It provides a command to generate a .mli file from the currently-open .ml file. It has saved me a tremendous amount of hassle. Note that the command is called “create interface file” if you’re looking for it in the Ctrl-Shift-P popup command menu.

3 Likes

There is also https://github.com/avsm/ocaml-print-intf which just had a new release on opam.

Regarding vscode, it’s worth considering ocaml-lsp-server and its related plugin which were recently announced on this forum.

2 Likes

thanks for all the help, everyone! The cmitomli command suggested by @mjambon seems to be perfect, but I’ll look at some of the other options as well.

Yeah, I plan on giving that newer plugin a shot after work on my current project is ~complete. Nothing worse than environment upgrades in the middle of things and the potential of breakage that goes along. :sweat_smile:

1 Like

This looks great, both cmitomli and ocaml-print-intf seem pretty easy to use in combination with dune. The latter also proposes the following command: dune exec -- ocaml-print-intf file.ml. Case solved. Thanks!

5 Likes

I got this:

$ dune exec -- ocaml-print-intf main.ml
Error: could not build main.ml's corresponding cmi using dune

even though the build had completed before without errors:

$ dune build --profile=release main.exe
$ _build/default/main.exe
Hello

My main.ml does use modules: my dune file reads (executable (name main) (modules main util) (libraries)).

I’m at the same time sorry to spam this thread (and a few others recently) and desperate that nothing seems to work as intended. (Please tell me if another place is more appropriate for these basic questions.)

This is all working really well now, just slightly differently. Just use the VSCode extension: GitHub - ocamllabs/vscode-ocaml-platform: Visual Studio Code extension for OCaml

The feature is called ‘Switch implementation/interface’. The naming is slightly misleading, it will generate a new file containing the interface for you if it doesn’t already exist.

3 Likes

If your file is an executable, what do you want to use the .mli for? In fact, dune 3 started generating empty .mli for executables to trigger the OCaml compiler to emit “unused identifier” warnings.

1 Like

I tried the proposed command in order to see if and how dune exec -- ocaml-print-intf ***.ml works.

Anticipating that some may question the question instead of answering it, I wrote above that my program does use modules (another file named util.ml in the same folder), but when I do dune exec -- ocaml-print-intf util.ml, I get the same error. (Actually, I was hoping that a command would generate all mli’s at once.)

Or was your remark a way to tell me that my dune file above is not correct ?

My questions may be too basic for this forum. Should I post on stackoverflow or another site instead ?

First, I’m presuming that this isn’t a common problem you want to solve all the time – just from time-to-time. Second, I’m not a dune user (I prefer Makefiles) but OTOH, I often have to reverse-engineer dune arcana when writing unit-tests that need to work the same with standard PPX rewriters, and the ones I write using Camlp5. So I have had the problem many times, of figuring out how some particular output file (cmo, cmi, whatever) got created.

Typically, I look in the “log” file and find the output filename, and it’ll appear first on the line that creates it. Then I can cut-and-paste that line and rerun it, or modify (e.g. remove the “-o blabla.cmo” and add a “-i” in order to get the .mli output.

Hope this helps. I recognize that this isn’t some fancy automated solution. OTOH, it’s pretty simple. An equivalent method works with Makefiles.

2 Likes