[Haskell-cafe] Platform Versioning Policy: upper bounds are not our friends

Twan van Laarhoven twanvl at gmail.com
Thu Aug 16 14:42:15 CEST 2012

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.


More information about the Haskell-Cafe mailing list