The check-pvp package
Check whether the version ranges used in the
matches the style of module imports
according to the Package Versioning Policy (PVP).
The tool essentially looks for any dependency
containers >=0.5 && <0.6
that allows the addition of identifiers to modules
within the version range.
Then it checks whether all module imports from
are protected against name clashes
that could be caused by addition of identifiers.
You must run the tool in a directory containing a Cabal package.
This requires that the package is configured, since only then the association of packages to modules is known. If you want to run the tool on a non-configured package you may just check all imports for addition-proof style.
$ check-pvp --include-all
It follows a detailed description of the procedure and the rationale behind it.
First the program classifies all dependencies
in the Cabal file of the package.
You can show all classifications with the
otherwise only problematic dependencies are shown.
A dependency like
containers >=0.5.0.3 && <0.5.1
does not allow changes of the API of
and thus the program does not check its imports.
Clashing import abbreviations are an exception.
containers >=0.5.1 && <0.6
requires more care when importing modules from
and this is what the program is going to check next.
This is the main purpose of the program!
I warmly recommend this kind of dependency range
since it greatly reduces the work
to keep your package going together with its imported packages.
containers >=0.5 or
containers >=0.5 && <1
are always problematic,
since within the specified version ranges identifier can disappear.
There is no import style that protects against removed identifiers.
An inclusive upper bound as in
containers >=0.5 && <=0.6
will also cause a warning, because it is unnecessarily strict.
If you know that
containers-0.6 works for you,
containers-0.6.1 will also work,
depending on your import style.
A special case of inclusive upper bounds are specific versions
The argument for the warning remains the same.
Please note that the check of ranges
is performed entirely on the package description.
The program will not inspect the imported module contents.
E.g. if you depend on
containers >=0.5 && <0.6
but import in a way that risks name clashes,
then you may just extend the dependency to
containers >=0.5 && <0.6.1
in order to let the checker fall silent.
If you use the dependency
containers >=0.5 && <0.6.1
then the checker expects that you have verified
that your package works with all versions of kind
and the version
Other versions would then work, too,
due to the constraints imposed by package versioning policy.
Let us now look at imports that must be protected against identifier additions.
The program may complain about a lax import. This means you have imported like
import Data.Map as Map
Data.Map may clash with other identifiers,
thus you must import either
import qualified Data.Map as Map
import Data.Map (Map)
The program emits an error on clashing module abbreviations like
import qualified Data.Map.Lazy as Map import qualified Data.Map.Strict as Map
This error is raised whenever multiple modules are imported with the same abbreviation, where at least one module is open for additions. Our test is overly strict in the sense that it also blames
import qualified Data.Map as Map import qualified Data.Map as Map
but I think it is good idea to avoid redundant imports anyway.
Additionally there are warnings on imports
that are consistent with large version ranges,
but complicate API changing updates of your dependencies.
You can disable these warnings with
The program warns about an open list of constructors as in
import Data.Sequence (ViewL(..))
Additions of constructors to
ViewL may also conflict with other identifiers,
but additions of constructors are considered API changes
since they may turn a complete case analysis into an incomplete one.
Similarly additionally class methods can turn a complete class instance
into a partial one.
Thus addition of constructors and class methods
require a version bump from
Nonetheless it is a good idea to import either
import Data.Sequence (ViewL(EmptyL, (:<)))
import qualified Data.Sequence as Seq
because you document the origin of identifiers this way. This is especially important when the imported identifiers are moved or removed in the future. If you use constructors only for constructions and not for pattern matches or if you only call class methods but do not define instances, then with explicit imports or qualified imports your modules survive such additions in your dependent packages without modifications.
More warnings are issued for hiding imports. The import
import Data.Map hiding (insert)
is not bad in the sense of the PVP,
but this way you depend on the existence of the identifier
although you do not need it.
If it is removed in a later version of
then your import breaks although you did not use the identifier.
Finally you can control what items are checked.
First of all you can select the imports that are checked.
Normally the imports are checked that belong to lax dependencies
containers >=0.5 && <0.6.
However this requires the package to be configured
in order to know which import belongs to which dependency.
Data.Map belongs to
You can just check all imports for being addition-proof
Following you can write the options
that allow to additionally check or ignore imports
from certain modules or packages.
These modifiers are applied from left to right.
--exclude-import=Prelude will accept any import style for
--exclude-dependency=foobar will ignore the package
say, because it does not conform to the PVP.
Secondly, you may ignore certain modules or components of the package
using the options
--exclude-module=Paths_PKG will exclude the Paths module
that is generated by Cabal.
I assume that it will always be free of name clashes.
The program cannot automatically filter out the
The program cannot find and check preprocessed modules.
The program may yield wrong results in the presence of Cabal conditions.
If this program proves to be useful
it might eventually be integrated in the
check command of
If you want to allow exclusively large version ranges, i.e.
>=x.y && <x.y+1,
then you may also add the option
GHC-Options fields of your Cabal file.
Unfortunately there is no GHC warning on clashing module abbreviations.
Related: There are programs that check PVP compliance of exports:
|Dependencies||base (>=4 && <4.7), Cabal (>=1.6 && <1.19), containers (>=0.2 && <0.6), explicit-exception (>=0.1.4 && <0.2), filepath (>=1.1 && <1.4), haskell-src-exts (==1.14.*), non-empty (>=0.1.3 && <0.3), transformers (>=0.2 && <0.4), utility-ht (>=0.0.10 && <0.1) [details]|
|Author||Henning Thielemann <email@example.com>|
|Maintainer||Henning Thielemann <firstname.lastname@example.org>|
|Source repository||head: darcs get http://code.haskell.org/~thielema/check-pvp/
this: darcs get http://code.haskell.org/~thielema/check-pvp/ --tag 0.0.1
|Uploaded||Sat Mar 1 09:26:38 UTC 2014 by HenningThielemann|
|Downloads||631 total (16 in the last 30 days)|
|Rating||2.0 (votes: 1) [estimated by rule of succession]|
|Status||Docs not available [build log]
Successful builds reported [all 10 reports]
Hackage Matrix CI
For package maintainers and hackage trustees