
On 16/08/12 14:07, Chris Smith wrote:
As a package author, when I release a new version, I know perfectly well what incompatible changes I have made to it... and those might include, for example:
1. New modules, exports or instances... low risk 2. Changes to less frequently used, advanced, or "internal" APIs... moderate risk 3. Completely revamped commonly used interfaces... high risk
Would adding a single convenience function be low or high risk? You say it is low risk, but it still risks breaking a build if a user has defined a function with the same name. I think the only meaningful distinction you can make are: 1. No change to public API at all, user code is guaranteed to "compile and work if it did so before". Perhaps new modules could also fall under this category, I'm not sure. 2. changes to exports, instances, modules, types, etc. But with the guarantee that "if it compiles, it will be correct" 3. changes to functionality, which require the user to reconsider all code. "even if it compiles, it might be wrong" For the very common case 2, the best solution is to just go ahead and try to compile it.
A. Cabal files should get a new "Compatibility" field, indicating the level of compatibility from the previous release: low, medium, high, or something like that, with definitions for what each one means.
You would need to indicate how large the change is compared to a certain previous version. "Moderate change compared to 0.10, large change compared to 0.9".
B. Version constraints should get a new syntax:
bytestring ~ 0.10.* (allow later versions that indicate low or moderate risk) bytestring ~~ 0.10.* (allow later versions with low risk; we use the dark corners of this one) bytestring == 0.10.* (depend 100% on 0.10, and allow nothing else)
Of course, this adds a good bit of complexity to the constraint solver... but not really. It's more like a pre-processing pass to replace fuzzy constraints with precise ones.
Perhaps it would be cleaner if you specified what parts of the API you depend on, instead of an arbitrary distinction between 'internal' and 'external' parts. From cabal's point of view the best solution would be to have a separate package for the internals. Then the only remaining distinction is between 'breaking' and 'non-breaking' changes. The current policy is to rely on major version numbers. But this could instead be made explicit: A cabal package should declare what API version of itself it is mostly-compatible with. To avoid forcing the creation of packages just for versioning, perhaps dependencies could be specified on parts of a package? build-depends: bytestring.internal ~< 0.11 and the bytestring package would specify what parts have changed: compatibility: bytestring.internal >= 0.11, bytestring.external >= 0.10 But these names introduce another problem: they will not be fine-grained enough until it is too late. You only know how the API is partitioned when, in the future, a part of it changes while another part does not. Twan