(Sub)module not found in test when using dune

Hi, I’m new to OCaml and I’m trying to understand how to use dune.

I constructed a minimal example that looks like this:

.
├── dune-project
├── lib
│   ├── dune
│   ├── mylib.ml
│   └── mymod.ml
└── test
    ├── dune
    └── test.ml

The dune-project file just contains one line: (lang dune 3.12)

The lib/dune file contains: (library (name mylib))

The test/dune file contains:

(test
 (name test)
 (libraries mylib))

The lib/mylib.ml file contains:

let one = 1
let _ = assert (one = 1)
let _ = assert (Mymod.two = 2)

And the lib/mymod.ml file, which is supposed to be a submodule, contains:

let two = 2

Now my test in test/test.ml looks like this:

let _ = assert (Mylib.one = 1)
let _ = assert (Mylib.Mymod.two = 2)

I get the following error:

$ dune test
File "test/test.ml", line 2, characters 16-31:
2 | let _ = assert (Mylib.Mymod.two = 2)
                    ^^^^^^^^^^^^^^^
Error: Unbound module Mylib.Mymod

Why does Mymod.two work inside lib/mylib.ml but not inside the test. What am I doing wrong?

See Using Modules (15 characters long now) - #2 by yawaramin

1 Like

From that other thread:

I do have a lib/dune file that says (library (name mylib)), so that would mean the module in lib/mylib.ml would be available as Mylib.Mylib and the module in lib/mymod.ml would be available as Mylib.Mymod. But that isn’t the case, i.e. this fails:

let _ = assert (Mylib.Mylib.one = 1)
let _ = assert (Mylib.Mymod.two = 2)

Instead, if I change (library (name mylib)) to (library (name foo)) (and modify test/dune accordingly), then these are suddenly available as Foo.Mylib and Foo.Mymod, i.e. this works:

let _ = assert (Foo.Mylib.one = 1)
let _ = assert (Foo.Mymod.two = 2)

Why does the first fail and the second work?

If you create a mylib.ml file for a lib named mylib in the dune file, it does not behave as a submodule but rather allows you to define the content of the top-level Mylib module. This allows you to re-export some submodules for instance, with a different name.

1 Like

So the solution then is to modify mylib.ml, I guess:

+module Mymod = Mymod
 let one = 1
 let _ = assert (one = 1)
 let _ = assert (Mymod.two = 2)

Would that be the idiomatic way to go?

1 Like

See Using Modules (15 characters long now) - #4 by cuihtlauac

2 Likes

I think so. If you want top-level values (or types) to appear in Mylib, then I think you need an explicit mylib.ml. And if you do that, then you need to define module aliases to export the inner modules.

I tried to find the relevant info in the dune documentation but it seems this is only mentioned in passing at the beginning of the documentation of the library stanza.

2 Likes

Thank you both for your help. Maybe the documentation could be improved. It was really confusing me a lot, but I think it now makes sense (to me).

The documentation can definitely be improved. If you like, you can send a PR and suggest some changes to their repo Welcome to Dune’s Documentation! — Dune documentation

Once I get better accustomed with the ecosystem, I may consider contributing. But I feel like I’m not at that point yet. :sweat_smile:

Hi @jbe

This matter is tentatively addressed in the new (draft) tutorial Libraries With Dune, which I have just announced. As @K_N noticed, this wasn’t very well documented.

1 Like