Hello, the dune team is working on adding
(include_subdirs qualified) support to dune and would like your feedback on some user facing details. I’ll explain how the feature works in this post, but you can also read the initial feature request for some background.
First, let’s review how wrapped libraries work. This is important because
(include_subdirs qualified) just generalizes the scheme to arbitrary directories. Suppose we have the following library:
(library (wrapped true) ;; this is the default. it's added here for clarity (name foolib))
By default, dune will make every single module in this library available under
Foolib - e.g.
Foolib.X. In this example, the “library interface” module is
Foolib and it is always present. In this example, it is generated. But it can also be written by hand:
$ cat foolib.ml (* We can choose to export whatever we want *) module X = X
The advantage of hand writing this interface module is of course tighter control over the interface of the library. The disadvantage is that it has to be manually written.
(include_subdirs qualified) generalizes the above scheme. In particular, one may introduce a directory with modules. For example:
$ ls foo.ml sub/ x.ml y.ml
Inside foo.ml, we’ll refer to
y.ml we’ll be able to refer to each other in an unqualified manner (
Y). Naturally, the module
Sub will also be an interface module and the user will have the option to write it manually. This is where we get some options.
Given the example above, where should the user write the interface module for
Sub and how should it be referred to in dune files? I’ll list two options and briefly describe their advantages:
sub/sub.ml- this would be most similar to how we handle the toplevel library interface. It also maintains the invariant that every directory has at most one interface file.
sub.ml- this module would live in the same directory as
sub/and would allow
sub/sub.mlto exist as
Sub.Sub. I think this behavior is more intuitive to users.
Finally, how should we refer to such modules in dune files? For example, in dune files we can set per module preprocessing or mark some modules as private. How should we make the
(library (name foolib) (private_modules foo) ;; or this (private_modules foo.foo))
If the interface modules exists at
sub/sub.ml, then we should probably just forbid
foo.foo. While if the interface module is
sub.ml, both paths are allowed and simplify refer to different modules (
My questions to the community:
Which scheme do you think is more natural?
Do you have any other comments about the feature?