Dune/OPAM package layout with 'extension' sub-libraries?

opam
package-design
dune
library
#1

Hi all,

Suppose I’m making a package that has one main library L, consisting of several different modules L.Foo, L.Bar, L.Baz, etc and with a certain set of dependencies; I then have a few smaller extension libraries A, B, and ‘C’, each with their own modules and dependencies.

The concrete example here is that I’m re-architecting travesty such that the main library contains monadic traversals and other such things and depends only on Base, but there’ll also be satellite libraries that include extension overlays for adding those traversals to Base containers, to Core_kernel containers, and also building new containers such as zippers and whatnot. So, I have a Travesty library, a Containers library, a Base_exts one, and a Core_kernel_exts one.

(The Core_kernel container depends on Core_kernel, whereas the other libraries don’t, so there’s an actual dependency difference here—but working out how to do that properly in opam is another question for another day.)

Looking around, the usual idiom for multiple distinct but related libraries in one package is to public-name them package-name.library-name in the dune config, give them the top-level module and name of library-name, and then refer to them pretty much everywhere in code as library-name. So, I’d have travesty.containers as my public-name, containers as my name, and refer to, say, 'a Containers.Singleton.t.

This would work nicely if the libraries were sufficiently coherent on their own to spin out into their own namespaces. But, in my case, they really are just satellites of my Travesty library, with names that make no sense outside of the Travesty context (and, worse, would pollute the namespace and clash with other libraries like containers!), and ideally I’d want to refer to them as Travesty.Containers.Singleton.t etc.

What would be the most idiomatic thing to do here? The way I’ve currently done it in travesty, which feels kind of horrible, is:

  • public-name of travesty.containers
  • name of travesty_containers
  • if I’m importing it from somewhere else, it’ll be Travesty_containers.Singleton.t

Is there an easier way?

I suppose one thing I could do is make the travesty library an umbrella for all the other packages, containing the union of their dependencies and re-exporting things into their correct positions in the hierarchy. I’d then move what is currently in the travesty library to, say, travesty.common. This seems a bit baroque, though, and, unless there’s some opam magic that means that it can switch off re-exports of the core_kernel extensions unless core_kernel is present, it’ll make using the more minimal fragments of the library somewhat unpleasant.

#2

If you disable warning 49, you can write module Containers = Travesty_containers in the umbrella travesty.ml file without declaring a dependency on travesty.containers . Though, I personally wouldn’t recommend doing this sort of things. In the long run, it might cause more troubles than it helps.

1 Like
#3

Yeah, I’d rather avoid anything that heavyweight :sweat_smile: I’ll probably just keep as-is for now; the library’s in a pretty heavy state of breakable change flux anyway, so it’s probably ok if I get it sub-optimal to begin with.