Package versioning policy
The goal of a versioning system is to inform clients of a package of changes to that package that might affect them, and to provide a way for clients to specify a particular version or range of versions of a dependency that they are compatible with.
Cabal provides the raw materials for versioning: it allows packages to specify their own version, and it allows dependencies that specify which versions of the dependent package are acceptable. Cabal will select dependencies based on the constraints.
What is missing from this picture is a policy that tells the library developer how to set their version numbers, and tells a client how to write a dependency that means their package will not try to compile against an incompatible dependency. For some time there has been an informal policy in use in the Haskell community, but it became clear that we were running into trouble with incorrectly-specified dependencies and unbuildable packages, so this page is an attempt to formalize the policy.
A package version number should have the form A.B.C, and may optionally have any number of additional components, for example 22.214.171.124 (in this case, A=2, B=1, C=0). This policy defines the meaning of the first three components A-C, the other components can be used in any way the package maintainer sees fit.
Version number ordering is already defined by Cabal as the lexicographic ordering of the components. For example, 2.1 > 1.3, and 2.1.1 > 2.1. (The Data.Version.Version type and its Ord instance embody this ordering).
A.B is known as the major version number, and C the minor version number. When a package is updated, the following rules govern how the version number must change relative to the previous version:
- If any entity was removed, or the types of any entities or the definitions of datatypes or classes were changed, or instances were added or removed, then the new A.B must be greater than the previous A.B. Note that modifying imports or depending on a newer version of another package may cause extra instances to be exported and thus force a major version change.
- Otherwise, if only new bindings, types, classes or modules were added to the interface, then A.B may remain the same but the new C must be greater than the old C.
- Otherwise, A.B.C may remain the same (other version components may change).
Hence A.B.C uniquely identifies the API. A client that wants to specify that they depend on a particular version of the API can specify a particular A.B.C and be sure of getting that API only. For example, build-depends: mypkg >= 2.1.1 && < 2.1.2.
Often a package maintainer wants to add to an API without breaking backwards compatibility, and in that case they can follow the rules of point 2, and increase only C. A client can specify that they are insensitive to additions to the API by allowing a range of C values, e.g. build-depends: base >= 2.1.1 && < 2.2.
Dependencies in Cabal
When publishing a Cabal package, you should ensure that your dependencies in the build-depends field are accurate. This means specifying not only lower bounds, but also upper bounds on every dependency.
At some point in the future, Hackage may refuse to accept packages that do not follow this convention. The aim is that before this happens, we will put in place tool support that makes it easier to follow the convention and less painful when dependencies are updated.
To minimize breakage when new package versions are released, you can use dependencies that are insensitive to minor version changes (e.g. foo >= 1.2.1 && < 1.3). However, note that this approach is slightly risky: when a package exports more things than before, there is a chance that your code will fail to compile due to new name-clash errors. The risk from new name clashes tends to be small, and you can always eliminate it by using explicit import lists if you want.
To make specifying dependencies easier, we probably want the extensions to the dependency syntax suggested by Thomas Schilling:
- script to check for API changes in gtk2hs: http://darcs.haskell.org/gtk2hs/tools/apidiff/