qualified imports, PVP and so on

Edward Kmett ekmett at gmail.com
Wed Feb 26 11:57:19 UTC 2014


Michael,

Technically it'd have to do a major version bump to add the instance.

Herbert,

That said, transitively bumping all the dependent packages whenever anyone
upstream's guts change in ways you may never know about isn't a palatable
option. It requires every user of every library to know about every
instance or import statement that could transitively drag along an orphan
even in private modules. This just isn't a realistic model of user behavior.

I'm unwilling to accept the corollary that I cannot be compatible with both
the current release of a package and the current platform if there have
been any instances added in between.

If the user wants to lock down the instances they can fix the version of
the upstream dependency that is supplying it.

The way I see it, if I don't supply the data type, and I don't supply the
class, then using a class is fine in my API across major versions.
Otherwise nobody can ship anything that crosses more than even one base
version let alone versions of other packages.

The only breakages that can occur be introduced are all due to orphan
instances.

What you propose when carried through to its logical conclusion would
basically kill all development between platform releases.

-Edward




On Wed, Feb 26, 2014 at 5:42 AM, Michael Snoyman <michael at snoyman.com>wrote:

>
>
>
> On Wed, Feb 26, 2014 at 12:22 PM, Herbert Valerio Riedel <hvr at gnu.org>wrote:
>
>> On 2014-02-26 at 10:45:40 +0100, Michael Snoyman wrote:
>>
>> [...]
>>
>> >> >> ...are you simply referring to the fact that in order to guarantee
>> >> >> PVP-semantics of a package version, one has to take care to
>> restrict the
>> >> >> version bounds of that package's build-deps in such a way, that any
>> API
>> >> >> entities leaking from its (direct) build-deps (e.g.  typeclass
>> instances
>> >> >> or other re-exported entities) are not a function of the "internal"
>> >> >> degree of freedoms the build-dep version-ranges provide? Or is there
>> >> >> more to it?
>>
>> [...]
>>
>> >> From my point of view, I'd argue that
>> >>
>> >>  a) 'persistent' failed to live up to the "spirit" of the PVP contract,
>> >>     i.e. to expose a "contact-surface" which satisfies certain
>> >>     invariants within specific package-version ranges.
>>
>> > How would persistent have done better? AFAICT, the options are:
>> >
>> > 1. Do what I did: state a true version dependency on monad-logger, that
>> it
>> > works with both version 0.2 and 0.3.
>>
>> Yes, "persistent" itself does in fact work with both major versions of
>> "monad-logger", but alas the API reachable through depending solely on
>> "persistent" leaks details of the underlying monad-logger version used.
>>
>> ...but the PVP's primary statement is define how a package shall behave
>> from the point of view of its users (where by user I mean package
>> build-depending on `persistent`). So...
>>
>> > 2. Constrain it to one or the other, which would be a falsehood that
>> would
>> > restrict users' ability to use the package.
>>
>> ...this is would actually be, what I'd interpret the PVP to
>> expect/require from "persistent" in order to satisfy its goal to shield
>> the package's users from incompatible changes.
>>
>> > Let's try it a different way. If transformers removed a MonadIO instance
>> > between version 2 and 3 of the library, should that mean that every
>> single
>> > package with type signatures involving MonadIO should be constrained to
>> one
>> > specific version of transformers?
>>
>> yes, that'd be what I'm suggesting here (the [1] footnote is a different
>> suggestion for the same problem though)
>>
>>
> So let's analyze how far you want to go here. Imagine if version 0.3.0 of
> transformers did not have a MonadIO instance for StateT, and version 0.3.1
> added it. Now some library has a function:
>
> myFunc :: MonadIO m => Int -> m String
>
> What versions of transformers is it allowed to work with? If it allows
> version 0.3.0 and 0.3.1, and a user depends on the presence of the MonadIO
> StateT instance, the build can be broken by moving back to version 0.3.0
> (which may be demanded by some other packages dependencies). This is simply
> the reverse of the monad-logger situation, where an instance was added
> instead of being removed. I don't see a reasonable solution to this
> situation... well, besides everyone just trusting a curator to build all of
> these packages for them.
>
> And just to be clear: if persistent had bumped its lower version bound on
> monad-logger, then users still on the old version of monad-logger would be
> unable to upgrade, and for no real reason. persistent would have required a
> major version bump most likely[1], which would have caused all packages
> downstream from it to do version bumps as well.
>
> Forgetting about my position as a library author, or as a Stackage
> maintainer, and speaking purely as a library *user*, this would be a
> terrible situation to be in.
>
> [1] That's another hole in the PVP I think. It doesn't explicitly address
> the issue of an API change by eliminating compatibility with a previously
> accepted dependency, but I've seen huge breakages occur due to this.
>
>
>> >>  b) However, the PVP can be blamed as well, as in its current form it
>> >>     doesn't explicitly address the issue of API-leakage from transitive
>> >>     build-dependencies. [1]
>> >>
>> >> The question for me now is whether the PVP is fixable in this respect,
>> >> and at what cost.
>> >>
>> >> Moreover, it seems to me, it always comes down to type-class instances
>> >> causing most problems with the PVP (either by requiring version-bump
>> >> cascades throughout the PVP-adhering domain of Hackage, or by their
>> hard
>> >> hard to constraint leakage through package module/boundaries); maybe we
>> >> need address this issue at the language-level and provide some facility
>> >> for limiting the propagation of type-class instances first.
>> >>
>> >>
>> > There's one other issue, which is reexports. As an extreme example,
>> imagine:
>> >
>> > * Version 1 of the foo package has the Foo module, and it exports foo1
>> and
>> > foo2.
>> > * Version 2 of the foo package has the Foo module, and it exports foo1.
>> > * Version 1 of the bar package as the Bar module, defined as:
>>
>> Yes, I'm well aware of this problem, but that's easier to control, as
>> you can use explicit import/export lists to constraint what entities you
>> expose to direct users of your package. That's what I'm doing e.g. in
>>
>>
>> http://hackage.haskell.org/package/deepseq-generics-0.1.1.1/docs/Control-DeepSeq-Generics.html
>>
>> where I'm explicitly naming the entities I re-export from
>> Control.DeepSeq for convenience. (However, I'm lacking such a facility
>> for instances)
>>
>>
> Agreed, the reexport issue is something that can be dealt with, whereas
> typeclasses don't have such a facility right now. I just wanted to point it
> out to make sure we were considering all issues.
>
>
>> >
>> > module Bar (module Foo) where
>> > import Foo
>> > * According to the PVP, the bar package can have a version bound on foo
>> of
>> > `foo > 1 && < 2.1`.
>> > * User code that depends on foo2 being exported from Bar will be broken
>> by
>> > the transitive update of foo.
>> >
>> > The example's extreme, but it's the same basic problem as typeclass
>> > instances.
>>
>> [...]
>>
>> >>  [1]: An alternative to what I'm suggesting in 'a)' (i.e. that it'd be
>> >>       `persistent`'s obligation), could be that the package you
>> >>       mentioned (which broke due to monad-logger having a non-monotonic
>> >>       API change), might become required to include packages supplying
>> >>       the instances they depends upon in their build-depends, thus
>> >>       turning an transitive dep into a direct dependency.
>>
>> > I don't think I follow this comment, sorry.
>>
>> I'm basically just saying, that the package which used "persistent",
>> ought to add "monad-logger ==0.2.*" to its direct build-dependencies, as
>> it depends on an instance provided by monad-logger. The huge down-side
>> is, that you'd have to know about type-class instances leaked through
>> persistent, in order to know that'd you have to add some of
>> "persistent"'s transitive build-depends to your own package, in order to
>> safe yourself from missing out on type-class instances.
>>
>
> And my approach is that the only sane way to create repeatable builds is
> to *always* list the exact versions of all packages you depend upon. And in
> my opinion, it's far more important to ensure that the code behaves the
> same way than that it simply builds. The only real way to do that is to
> always use the same versions of all dependencies.
>
> Michael
>
> _______________________________________________
> Libraries mailing list
> Libraries at haskell.org
> http://www.haskell.org/mailman/listinfo/libraries
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/libraries/attachments/20140226/04a8a1ae/attachment-0001.html>


More information about the Libraries mailing list