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.
Wrapped Libraries
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.
Qualified Subdirectories
The stanza (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 Sub.X
and Sub.Y
. While x.ml
and y.ml
we’ll be able to refer to each other in an unqualified manner (X
and 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.
Interface Modules for module groups
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 assub/
and would allowsub/sub.ml
to exist asSub.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 Sub
private?
(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 (sub.ml
or sub/sub.ml
).
My questions to the community:
-
Which scheme do you think is more natural?
-
Do you have any other comments about the feature?