qualified imports, PVP and so on (Was: add new Data.Bits.Bits(bitZero) method)

Henning Thielemann schlepptop at henning-thielemann.de
Fri Feb 28 20:10:09 UTC 2014

I got distracted by writing a tool that checks consistency of package 
dependencies and import styles. It's not yet perfect, but usable:


   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@ 
   otherwise only problematic dependencies are shown.
   A dependency like @containers >= && <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- 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 at .
   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 at .
   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
   > 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 
   You must instead import like
   > import Data.Sequence (ViewL(EmptyL, (:<)))
   > import qualified Data.Sequence as Seq
   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 you can enable a test for hiding imports
   with the @--pedantic@ option.
   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 @insert@
   although you do not need it.
   If it is removed in a later version of @containers@,
   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
   like @containers >=0.5 && <0.6 at .
   However this requires the package to be configured
   in order to know which import belongs to which dependency.
   E.g. @Data.Map@ belongs to @containers at .
   You can just check all imports for being addition-proof
   using the @--include-all@ option.
   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.
   E.g. @--exclude-import=Prelude@ will accept any import style for 
   and @--exclude-dependency=foobar@ will ignore the package @foobar@,
   say, because it does not conform to the PVP.
   Secondly, you may ignore certain modules or components of the package
   using the options
   @--exclude-benchmarks at .
   E.g. @--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.
   Known problems:
   * The program cannot automatically filter out the @Paths@ module.
   * The program cannot find and check preprocessed modules.
   * The program may yield wrong results in the presence of Cabal 
   If this program proves to be useful
   it might eventually be integrated in the @check@ command of 
@cabal-install at .
   See <https://github.com/haskell/cabal/issues/1703>.
   If you want to allow exclusively large version ranges, i.e. @>=x.y && 
   then you may also add the option @-fwarn-missing-import-lists@
   to the @GHC-Options@ fields of your Cabal file.
   See <https://ghc.haskell.org/trac/ghc/ticket/4977>.
   Unfortunately there is no GHC warning on clashing module abbreviations.
   See <https://ghc.haskell.org/trac/ghc/ticket/4980>.

More information about the Libraries mailing list