If a maintainer adds a brand-new public sub-library (with orphan instances) to an existing package, does the package require a major version bump?
Looking at the PVP decision tree, it seems that exporting new orphan instances leads to “yes, a major version bump is required”. But since it’s not possible for a completely new public sub-library (of an existing package) to have existing users, is there any way that the publication of these orphan instances could break existing code?
Context: I’m preparing a new release of the monoidmap package to add support for JSON serialisation, compatible with classes defined by aeson. To keep the dependency footprint small for users of monoidmap who don’t need JSON support (and to retain the future possibility of supporting other JSON libraries), I’d like to add orphan ToJSON and FromJSON instances to a new public sub-library called monoidmap-aeson. This way, people who want JSON support via aeson can add a dependency on monoidmap:monoidmap-aeson, while others who don’t can continue depending on just the lightweight monoidmap core library.
I’m somewhat inclined to perform a major version bump here, if only because adding a new public sub-library (with orphan instances) seems (subjectively) to be more of a major change than just adding a new function. But suppose I perform a minor version increment, could anything break?
I’m very interested to get a second opinion on this from others in the community. Thanks for reading!
Public sublibraries are, essentially, a hack to make a new package without actually creating a separate package and having automatic syncing of versions with the main library.
So I think your assessment is correct: I can’t see a scenario where introduction of a new sublibrary can break something. Now that would be different if there was something like a wildcard import. But there isn’t. build-depends: monoidmap will only import the main library.
Expressions like build-depends: monoidmap:{monoidmap,monoidmap-aeson} ^>= 0.0 will lock the minimum version to the first version that has said sublibrary (afaiu). Maybe it’s worth testing how cabal behaves here. So it could be surprising (maybe) for an end user who adds monoidmap-aeson, but doesn’t increase the minimum bound… to find out the solver picked a new minimum bound.
Whether to do a major version bump or not then boils down to preference:
major version bump seems morally correct and is also a form of advertisement
but it causes churn for your end users (unless you maintain both major versions indefinitely)
I agree that, strictly speaking, it wouldn’t require a major version bump.
To keep the dependency footprint small for users of monoidmap who don’t need JSON support
I have one question about this. If when developoing an application I only depend on monoidmap but not monoidmap:monoidmap-aeson, does the constraint solver still have to solve for a valid version of aeson, even if it doesn’t need to install it?
Thanks @danidiaz. My naive expectation was that a dependency on monoidmap alone wouldn’t require the constraint solver to solve for a valid version of aeson, but I haven’t been able to confirm this experimentally.
I’ve been experimenting with a minimal empty package that just depends on base and the latest published version of monoidmap (monoidmap-0.0.3.0). Running cabal freeze for the minimal package generates a freeze file that does include aeson. I suspect this is because the test suite of monoidmap has a transitive dependency on aeson.
In any case, I’m starting to think it may be simpler to just publish monoidmap-aeson as a standalone package, for unrelated but practical reasons:
Documentation
It’s relatively easy to build and publish haddock documentation for a standalone package as part of CI. (With public sublibraries, I haven’t yet figured out a reliable way to build integrated documentation for both the main library and public sublibraries.)
Independent versioning
If in future I need to publish updates to monoidmap-aeson, I can do so without having to bump the version of monoidmap.
Independent deprecation
If (for whatever reason) I eventually need to deprecate the monoidmap-aeson package, I won’t need to bump the major version of monoidmap. (Whereas with the public sublibrary solution, this would presumably require a major version bump.)
Do we even have any proper way of deprecating a sublibrary? Because simply removing it may force existing users (e.g. in the above case) into an old version of monoidmap forever, until they figure out it doesn’t exist anymore in recent versions.
I think this is one of the key determinants of whether public sub-libraries are the right approach. If you want to version it separately, you really need a separate package. On the other hand, sometimes there are packages that are in practice always released together.