
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)
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-Dee... where I'm explicitly naming the entities I re-export from Control.DeepSeq for convenience. (However, I'm lacking such a facility for instances)
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.