I second that sentiment of appreciation. As much as we all disagree on some things, we all agree you make a tremendous amount of neat code available to the community.
On Friday, February 28, 2014, Edward Kmett <ekmett@gmail.com> wrote:
I just want to say thank you for writing this tool.I may not agree with your interpretation of the PVP on this particular issue, and will probably only use it on a couple of smaller packages that have almost no imports, but at least putting code out there to help those who do want to work in that style is a very helpful and valuable thing.-EdwardOn Fri, Feb 28, 2014 at 3:10 PM, Henning Thielemann <schlepptop@henning-thielemann.de> wrote:
I got distracted by writing a tool that checks consistency of package dependencies and import styles. It's not yet perfect, but usable:
https://hackage.haskell.org/package/check-pvp
Description:
Check whether the version ranges used in the @Build-Depends@ field
matches the style of module imports
according to the Package Versioning Policy (PVP).
See <http://www.haskell.org/haskellwiki/Package_versioning_policy>.
The tool essentially looks for any dependency
like @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 @containers@
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.
.
> $ check-pvp
.
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 @--classify-dependencies@ option,
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 @containers@
and thus the program does not check its imports.
Clashing import abbreviations are an exception.
.
The dependency @containers >=0.5.1 && <0.6@
requires more care when importing modules from @containers@
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.
.
Dependencies like @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,
then @containers-0.6.0.1@ or @containers-0.6.1@ will also work,
depending on your import style.
A special case of inclusive upper bounds are specific versions
like in @containers ==0.6@.
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 @0.5.x@
and the version @0.6.0@.
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
.
Additions to @Data.Map@ may clash with other identifiers,
thus you must import either
.
> import qualified Data.Map as Map
.
or
.
> import Data.Map (Map)
.
The program may complain about an open list of constructors as in
.
> import Data.Sequence (ViewL(..))
.
Additions of constructors to @ViewL@ may also conflict with other identifiers.
You must instead import like
.
> import Data.Sequence (ViewL(Empt