Consider the example of http-conduit-2.0.* which reexports entities such as parseUrl from http-client >= 0.2 && < 0.5.
The problem here is that the type-signature of parseUrl changed between http-client-0.2 and http-client-0.3 and this was in fact properly signaled by a major version increment in http-client's package version; however, the maintainer of http-conduit carelessly reexported parseUrl via the public API of http-conduit-2.0.* but failed to ensure that the reexported entity had a well-defined type-signature by setting appropriate version bounds on http-client (the current < 0.5 bounds were added by Trustees without taking into account the issue at hand).
In other words, as a consumer of http-conduit who correctly declares the dependency as
build-depends: http-conduit == 2.0.*
and import
import Network.HTTP.Conduit (parseUrl)
you now may either have imported
parseUrl :: Failure HttpException m => String -> m Request
or
parseUrl :: MonadThrow m => String -> m Request
depending on whether http-conduit ends up depending on http-client == 0.2.* or http-client == 0.3.*.
This clearly represents a failure to uphold the API versioning contract which morally denotes that the advertised API version shall be a good identifier for the exported API of a package.
A consumer can in principle use the hacky workaround of controlling http-client's transitive deps for all its reexported entities, i.e. by declaring a fake/artificial build-depends: http-client == 0.2.*.
However, this has multiple problems and is merely treating the symptoms: As a consumer you'd need to become aware of any reexported entities of a package you depend on, and start depending on the transitive deps where these come from (and be constantly vigilant about dependency starting to add reexported entities in later versions); For one this breaks the abstraction that packages represent but more importantly this shifts the burden to every consumer and is therefore clearly non-optimal; also it's not allowed to add such new dependencies via metadata revisions which consequently causes a huge problem for Hackage curation by Trustees as there's no benign way to fix such situations.
Long story short, ensuring the well-definedness of exported and reexported API entities is the responsibility of the API provider in order for the Hackage ecosystem to work efficiently.
A concrete example of a consumer is aws-0.8.4 and aws-0.8.5 which end up in this pernicious situation:
[ 1 of 64] Compiling Aws.Ec2.InstanceMetadata ( Aws/Ec2/InstanceMetadata.hs, dist/build/Aws/Ec2/InstanceMetadata.o )
Aws/Ec2/InstanceMetadata.hs:20:41:
No instance for (exceptions-0.10.2:Control.Monad.Catch.MonadThrow
(ResourceT IO))
arising from a use of ‘HTTP.parseUrl’
In a stmt of a 'do' block:
req <- HTTP.parseUrl ("http://169.254.169.254/" ++ p ++ '/' : x)
In the expression:
do { req <- HTTP.parseUrl
("http://169.254.169.254/" ++ p ++ '/' : x);
HTTP.responseBody <$> HTTP.httpLbs req mgr }
In an equation for ‘getInstanceMetadata’:
getInstanceMetadata mgr p x
= do { req <- HTTP.parseUrl
("http://169.254.169.254/" ++ p ++ '/' : x);
HTTP.responseBody <$> HTTP.httpLbs req mgr }
but there doesn't appear to be a metadata revision possible to perform on aws-0.8.[45] which can prevent aws depending on the combination of http-client-0.3.* and http-conduit-2.0.0.*, as aws doesn't directly build-depends on http-client.
Also this is quite problematic as aws-0.8.4 is the highest version in the 0.8.* major version range which is compatible with GHC 7.4.2 (see image below) and would therefore likely be selected by the cabal solver when a aws < 0.9 upper bound is in place:
Consequently, in order to treat the root cause we'd instead have to ensure that http-conduit-2.0.0.* doesn't export ill-defined type-signatures of its public API entities. And the only way seems to be to revise http-conduit-2.0.0.* to constraint it to http-client-0.2.* (or alternatively to http-client-0.3.* -- which in this case however would break consumers which were coded against the original http-client-0.2 typesignatures).
Consider the example of
http-conduit-2.0.*
which reexports entities such asparseUrl
fromhttp-client >= 0.2 && < 0.5
.The problem here is that the type-signature of
parseUrl
changed betweenhttp-client-0.2
andhttp-client-0.3
and this was in fact properly signaled by a major version increment inhttp-client
's package version; however, the maintainer ofhttp-conduit
carelessly reexportedparseUrl
via the public API ofhttp-conduit-2.0.*
but failed to ensure that the reexported entity had a well-defined type-signature by setting appropriate version bounds onhttp-client
(the current< 0.5
bounds were added by Trustees without taking into account the issue at hand).In other words, as a consumer of
http-conduit
who correctly declares the dependency asand import
you now may either have imported
or
depending on whether
http-conduit
ends up depending onhttp-client == 0.2.*
orhttp-client == 0.3.*
.This clearly represents a failure to uphold the API versioning contract which morally denotes that the advertised API version shall be a good identifier for the exported API of a package.
A consumer can in principle use the hacky workaround of controlling
http-client
's transitive deps for all its reexported entities, i.e. by declaring a fake/artificialbuild-depends: http-client == 0.2.*
.However, this has multiple problems and is merely treating the symptoms: As a consumer you'd need to become aware of any reexported entities of a package you depend on, and start depending on the transitive deps where these come from (and be constantly vigilant about dependency starting to add reexported entities in later versions); For one this breaks the abstraction that packages represent but more importantly this shifts the burden to every consumer and is therefore clearly non-optimal; also it's not allowed to add such new dependencies via metadata revisions which consequently causes a huge problem for Hackage curation by Trustees as there's no benign way to fix such situations.
Long story short, ensuring the well-definedness of exported and reexported API entities is the responsibility of the API provider in order for the Hackage ecosystem to work efficiently.
A concrete example of a consumer is
aws-0.8.4
andaws-0.8.5
which end up in this pernicious situation:works, but
fails with
but there doesn't appear to be a metadata revision possible to perform on
aws-0.8.[45]
which can preventaws
depending on the combination ofhttp-client-0.3.*
andhttp-conduit-2.0.0.*
, asaws
doesn't directlybuild-depends
onhttp-client
.Also this is quite problematic as
aws-0.8.4
is the highest version in the0.8.*
major version range which is compatible with GHC 7.4.2 (see image below) and would therefore likely be selected by the cabal solver when aaws < 0.9
upper bound is in place:Consequently, in order to treat the root cause we'd instead have to ensure that
http-conduit-2.0.0.*
doesn't export ill-defined type-signatures of its public API entities. And the only way seems to be to revisehttp-conduit-2.0.0.*
to constraint it tohttp-client-0.2.*
(or alternatively tohttp-client-0.3.*
-- which in this case however would break consumers which were coded against the original http-client-0.2 typesignatures).