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

Michael Snoyman michael at snoyman.com
Tue Feb 25 06:44:45 UTC 2014

On Mon, Feb 24, 2014 at 10:30 PM, Henning Thielemann <
schlepptop at henning-thielemann.de> wrote:

> Am 24.02.2014 20:56, schrieb Edward Kmett:
>  As far as I know the only serious proponent of using qualified imports
>> for all imports /all/ the time is you.
> First, there are some important packages like containers and bytestring
> that clearly are intended for qualified imports, and I really like that
> insertFM was replaced by Map.insert in the past. Second, your argument
> addresses a person not the issue.
>  Name conflicts don't affect you. We get that. We got that loud and clear
>> virtually every time the naming of pretty much anything has arisen on
>> this mailing list for the last few years.
> Sure, because there is no general discussion about the pros and cons of
> various naming styles. That's why it is discussed for every single
> identifier. How should a general discussion look like? What could be its
> outcome? That I am no longer allowed to propose the qualified import style?
>  That doesn't change the fact that your practice and common practice
>> diverge.
> And I challenge the common practice, because it prefers convenience for
> few package authors over convenience for many package user (that may not
> even be a Haskell programmer).
> For an example let me look at your lens package. You use unqualified and
> implicit imports. That is according to the PVP you would need to use tight
> version bounds like "containers >=0.4.0 && <", but you don't. That
> is, your package does not conform to the PVP. It implies that users may
> have to fix your package when one of the imported packages starts to export
> identifiers that clash with those from "lens". Of course, there is no need
> to conform to the PVP. But it works best if many people adhere to it.
> I have not written the PVP, that is, there must be at least one other
> person who cares. I understand the need for it and try to comply to it.
> Maybe I am in a minority. Looking at Hackage it seems I am in a minority.
> But I believe I am in the minority who cares whether packages work
> together. Shall I capitulate to the majority which does not seem to care
> about package interoperability?
> I am also ok if sloppy common practice happens in many Hackage packages. I
> do not need to use them. But I need to use 'base'.
This email seems to conflate a number of different issues together. I'd
like to address them separately.

Firstly, regarding naming style. As I see it, there are essentially three
camps on this one:

1. Short names that are intended to be imported qualified. Examples:
Data.Map, Data.ByteString.
2. Longer names that can be imported unqualified. Examples: Data.IORef,
3. Typeclass-based approaches that generalize across multiple libraries.
Examples: Data.Foldable, Data.Traversable.

The initial discussion came down to an argument between (1) and (2). What I
disagree in your email Henning is the implication that this "sloppy
practice" in base will somehow negatively affect you. As I see it, the sum
total of negative effect is that you'll be required to type a few extra
characters (e.g., zeroBits instead of zero). Am I missing something? Given
that (a) a huge amount of base already works this way, (b) having the
longer name will allow the unqualified import approach, and (c) it has a
small impact on those wanting unqualified imports, I'd come down with a
strong vote in favor of option (2).

Next is the issue of PVP. I am someone who has stopped religiously
following the PVP in the past few years. Your email seems to imply that
only those following the PVP care about making sure that "packages work
together." I disagree here; I don't use the PVP specifically because I care
about package interoperability.

The point of the PVP is to ensure that code builds. It's a purely
compile-time concept. The PVP solves the problem of an update to a
dependency causing a downstream package to break. And assuming everyone
adheres to it[1], it ensures that cabal will never try to perform a build
which isn't guaranteed to work.

But that's only one half of the "package interoperability" issue. I face
this first hand on a daily basis with my Stackage maintenance. I spend far
more time reporting issues of restrictive upper bounds than I do with
broken builds from upstream changes. So I look at this as purely a game of
statistics: are you more likely to have code break because version 1.2 of
text changes the type of the map function and you didn't have an upper
bound, or because two dependencies of yours have *conflicting* versions
bounds on a package like aeson[2]? In my experience, the latter occurs far
more often than the former.

My point here is: please don't try to frame this argument as "sloppy people
who hate compatibility" versus "PVP-adhering people who make Hackage
better." Some of us who have stopped following the PVP have done so for
very principled reasons, even if you disagree with them. (And Edward's
comments on maintenance effort is not to be ignored either.)

Three final points:

* I know that back in the base 3/4 transition there was good reason for
upper bounds on base. Today, it makes almost no sense: it simply prevents
cabal from even *trying* to perform a compilation. Same goes with libraries
like array and template-haskell, which make up most of the issue with
testing of GHC 7.8 Stackage builds[3]. Can any PVP proponent explain why
these upper bounds still help us on corelibs?
* If you're concerned about your production code base breaking by changes
on Hackage, you're doing it wrong. Basing your entire production build on
the presumption that Hackage maintainers perfectly follow the PVP and never
make any mistakes in new releases of their packages is a recipe for
disaster. You should be pinning down the exact versions of packages you
depend on. Greg Weber described a technique for this[4].
* The PVP doesn't in any way solve all problems. You can perfectly adhere
to the PVP and still experience breakage. I've seen a number of examples of
this in the past, mostly to do with the fact that you don't lock down the
versions of transitive dependencies, which can cause re-exports to include
new functions or expose (or hide) typeclass instances.


[1] And never makes any mistakes of course.
[2] This just occurred in Stackage.
[3] https://github.com/fpco/stackage/issues/128
[4] http://blog.docmunch.com/blog/2013/haskell-version-lockdown
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/libraries/attachments/20140225/a0f3ccdd/attachment-0001.html>

More information about the Libraries mailing list