[Haskell-cafe] How to determine correct dependency versions for a library?
Tobias Müller
troplin at bluewin.ch
Wed Nov 14 22:01:49 CET 2012
Clark Gaebel <cgaebel at uwaterloo.ca> wrote:
> To prevent this, I think the PVP should specify that if dependencies get
> a major version bump, the package itself should bump its major version
> (preferably the B field).
No, it has nothing to do with major/minor version bumps. It's just that if
you underspecify your dependencies, they may become invalid at some point
and you cannot correct them.
Overspecified dependencies will always remain correct.
Your suggested solution entirely defeats the purpose of underspecified
dependencies, namely that you _don't_ have to update your package every
time that a dependency is updated.
Also, note that in my toy scenario, the maintainer of package A now also
has to check the dependencies of package B. He must recognize that
B-2.5.3.0 has incorrect dependencies, and exclude this version from the
dependencies of B.
So just because the maintainer of B was too lazy, all projects that depend
on B have to insert special cases for bad versions of B.
> Hopefully, in the future, cabal would make a distinction between packages
> * used* within another package (such as a hashmap exclusively used to
> de-duplicate elements in lists) and packages *needed for the public
> API*(such as Data.Vector needed for aeson). That way, internal packages
> can update dependencies with impunity, and we still get the major version
> number bump of packages needed for the public API.
If you reexport an API, you should probably make very tight dependency
restrictions, if not just one single version.
For packages that are developed in parallel this is even more natural.
What is the advantage if cabal recognizes that? What could it do
differently that you cannot do already by just setting the appropriate
dependencies?
Tobi
> Hi Clark.
>
> > I think we just use dependencies [to specify] different things.
>
> If dependency version constraints are specified as a white-list -- i.e.
> we include only those few versions that have been actually verified and
> exclude everything else --, then we take the risk of excluding *too
> much*. There will be versions of the dependencies that would work just
> fine with our package, but the Cabal file prevents them from being used in the build.
>
> The opposite approach is to specify constraints as a black-list. This
> means that we don't constrain our build inputs at all, unless we know for
> a fact that some specific versions cannot be used to build our package.
> In that case, we'll exclude exactly those versions, but nothing else. In
> this approach, we risk excluding *too little*. There will probably be
> versions of our dependencies that cannot be used to build our package,
> but the Cabal file doesn't exclude them from being used.
>
> Now, the black-list approach has a significant advantage. In current
> versions of "cabal-install", it is possible for users to extend an
> incomplete black-list by adding appropriate "--constraint" flags on the
> command-line of the build. It is impossible, however, to extend an
> incomplete white-list that way.
>
> In other words: build failures can be easily avoided if some package
> specifies constraints that are too loose. Build failures caused by
> version constraints that are too strict, however, can be fixed only by
> editing the Cabal file.
>
> For this reason, dependency constraints in Cabal should rather be
> underspecified than overspecified.
>
> The blacklisting approach has one major disadvantage that noone has
> mentioned yet: Adding more restrictive constraints does not work, the
> broken package will be on hackage forever, while adding a new version
> with relaxed constraints works well.
>
> Consider the following example:
>
> A 1.1.4.0 build-depends: B ==2.5.* C ==3.7.* (overspecified) B 2.5.3.0
> build-depends: C ==3.* (underspecified) C 3.7.1.0
>
> Everything works nice until C-3.8.0.0 appears with incompatible changes
> that break B, but not A.
>
> Now both A and B have to update their dependencies and we have now:
>
> A 1.1.5.0 build-depends: B ==2.5.* C >=3.7 && <3.9 B 2.5.4.0
> build-depends: C >=3 && <3.8 C 3.8.0.0
>
> And now the following combination is still valid: A 1.1.5.0
> B 2.5.3.0 (old version)
> C 3.8.0.0
> Bang!
More information about the Haskell-Cafe
mailing list