Skip to main content

Hackage Metadata

Hackage, the Haskell package repository, maintains metadata about each package in the repository. Though the content of a package release in the repository is immutable, the metadata for the release is mutable. This blog entry summarizes my understanding and opinions about this design.

Immutable Package Releases

Once a package has been published in Hackage, the package itself cannot be changed or removed. In general, software developed using specific versions of packages on Hackage should not break/change due to dependency changes since the dependencies cannot be changed. Since packages cannot be removed, the Haskell ecosystem can avoid incidents like the infamous left-pad incident in the JavaScript ecosystem.

Since published package releases cannot be updated or removed, there has to be a way of dealing with mistakes. One safeguard that helps avoid publishing package releases with mistakes is candidate packages. Package maintainers can upload a package release as a candidate in order to confirm that it looks okay before publishing. Note that candidates are not normally used by Cabal/Stack, but you can specify a URL to the candidate tarball on Hackage to test it.

Currently, package maintainers are not forced to use candidates; package releases can be published directly. There has been talk about changing the workflow to always register a candidate before publishing a new package release. This would entail a few extra clicks for those who do not already use candidates, but it might help clean up the repository. There are many old candidates, and I suspect that most of them are simply forgotten.

Preferred and Deprecated Versions

Package maintainers can mark specific version ranges of a package as preferred, as well as mark specific versions of a package as deprecated. This provides another way of dealing with mistakes. Few packages make use of preferred version ranges, but many packages specify deprecated versions. Cabal takes this information into account when determining which versions of packages to use, but it is still possible to use any of the versions with constraints configured accordingly.

On Hackage, deprecated version links are displayed in red, while any non-preferred version links are displayed in (vivid) yellow. Other version links are displayed in purple. When the URL does not specify a version number, the latest preferred/non-deprecated version is displayed. Note that version text for the displayed version is always shown in bold black, even if it is deprecated or non-preferred. There are no indicates on the page that the version is deprecated or non-preferred, which is unfortunate.

Unless it is added to a package description or README, which I have yet to see, there is no explanation about why different versions are preferred or deprecated. Sometimes, I really wish that I could easily see such information. For example, the latest version of the time package (time-1.13) is deprecated. When I noticed it before, I figured that a new release would come soon. It has been deprecated for months without a new release, however, so I finally decided to look into it and figure out what is going on. I eventually found a GitHub issue with the explanation: the maintainer unnecessarily bumped the major version, which causes others to have to update their dependency version constraints, so the major release was deprecated and re-released with a minor version bump.

Deprecated Packages

Package maintainers can also mark whole packages as deprecated. This is very useful information to have when figuring out which dependencies to use when starting a new program.

Package Revisions

Hackage also allows package maintainers (and Hackage trustees) to change certain metadata defined in a package .cabal file, called a revision. The metadata on Hackage is changed, but the package release is not changed. If you download the tarball, the contained .cabal file is the original, without any changes made by revisions.

I have just recently discovered two very useful references about revisions:

The first document specifies what metadata can be changed. I suspect that most revisions are done to change dependency bounds, which can be tightened or loosened. Sometimes the bounds are modified to ensure that Cabal does select a deprecated package. For example, a revision to time-1.13 changes the bounds for base to the following.

<0 && >=4.13 && <5

I point this out explicitly because one can sometimes not realize that a certain version is deprecated when testing compatibility. If you see errors with such impossible version constraints, check to see if you are attempting to use a deprecated version.

In the past, I have not been a fan of using revisions to bump the upper bounds of dependencies. I would like project metadata to match the Hackage metadata, so I have always created new releases in such cases. The second document linked above states that metadata should be minimized in order to keep the overall system sustainable, however. The wording indicates that many releases may have performance implications, not just human factors. A “guiding principle” states:

New package releases ought to be avoided when a meta-data revision would suffice

Though I personally prefer making new releases, I will start to use revisions to bump the upper bounds of dependencies instead, following the correct way of managing metadata.


Travis Cardwell


Related Blog Entries