Assuming the plan is to get http-client/wai to adopt the new types, then it doesn’t sound like renaming the packages will help. In rust, this will result in a there are multiple different versions of crate http-types in the dependency graph error if the user doesn’t have the same http-types as its dependencies.
I am not a Rust user, but all the information I can find online points to Rust allowing multiple different versions of the same crate in the dependency graph (e.g., Articles - Stephen Coakley). Why wouldn’t that work in this case?
This is fine for internal libraries that are not re-exported. I assumed that a new http-types will come to end users through http-client or wai, which will requires using the same versions.
See also private dependencies which has a working prototype afaik. It’s a similar idea but rather than renaming, you can depend on a package and then guarantee that it’s not exposed in your public API, and then you can have multiple versions in the same build graph Private dependencies · Issue #4035 · haskell/cabal · GitHub
I don’t think it has to be limited to private/internal libraries. The APIs will be incompatible, but one could write a compat package to map types from one version to the other, or it may just be that the particular parts of the API that are incompatible are not actually used together. Again, from all my internet searching that seems to be what Rust does (e.g., it produces errors like this: https://stackoverflow.com/questions/78298605/rust-duplicate-dependencies).
FYI Go also had a similar idea. You can depend on multiple major versions of the same library, but the there is no renaming at the library level - multiple different versions are denoted with import.path/v1, import.path/v2, etc.
If you are expected to be able to use two different version of the same library in one process, then I assume that means that the contents of the two are disjoint (or the build infrastructure makes them so, i.e. makes ModuleA become ModuleA.v1 and ModuleA.v2), so it seems much simpler for these to just be packaged as two separate libraries–that’s easy to understand, and no new tooling is required.
This is qualitatively different than just a major version change–a major version change means that things can break, but this means that de facto everything has changed.
Or to put it another way, if I can use multiple version of the same library side-by-side in an application, then that really strains the notion of it being “the same library”.
Well yes, I think thats my point. Having this module system is even more general. You can have two completely different packages but also one package with two different versions. As long as the import distinguishes between the two, and you can also export them under different namespaces.
The problem is that every breaking change currently requires lock-step upgrades throughout the ecosystem (See the previous thread linked at the top). Temporarily allowing two versions of the same package to be used enables more incremental upgrades.
can easily blow up binary size… now I have multiple versions of conduit and amazonka in my depgraph?
makes it harder for the end user to constrain dependencies… I suspect we need to add syntax to cabal.project (constrain a dependency for the whole depgraph or only for a single package)
probably makes solver errors even more peculiar
mixing two versions of a package is a potential footgun
We’re trying to solve a social issue with a technical solution again is what I feel. Library maintainers should strive for portability (support multiple major versions of a dependency), but they also need to know which support window makes sense and what versions are actually supported upstream.
All of this requires some form of communication, either automated or coordinated.
Sadly, hackage is not really a collaboration platform.
Not to climb on to a dead horse, but doesn’t backpack/mixins already make this possible? Or does it get disallowed because they’re the same underlying package…
I’m not sure what PR @fendor is referring to. The private dependencies issue mentions mixins, but not how to use them to use the modules of two different versions of a dependency.
I think we should think whether this is a good idea. Rust’s ability to import several versions of a single package provides an easy baseline. But also has problems. Like, multiple versions of a single package can in turn depend on multiple version of sub dependencies, which can result in an explosion of dependencies size
Something i have been exploring in idris2, is making new library versions backwards compatible with old ones. In idris2 you can make a multiple functions with the same name. If a single function needs to be updated to have more or different arguments , you can make a new one with the right ones, then deprecate the old one, remove it later down the line
There are similar options for data types and whatnot
Haskell does not have function overloading, but it can be emulated by defining a typeclass to emulate a function. When needing to update the arguments of a function , you could instead define a typeclass to emulate an overloaded version of the function. Several versions later, after your library has removed the old function, the new one could be converted to a normal function . There are even options to smooth over this transition. Extreme edge cases would care whether something is a function or typeclass emulating a function, but this could be dealt with
What if we took the existing *-compat semi-solution to the backward compatibility problem and made it an pervasive shim? Suppose there’s a dependency graph {A -> {B, C}, {B -> D.1}, {C -> D.2}} where D.1 and D.2 are two versions of D, and there’s a D-1-shim library that can act as a full replacement for D.1 while depending on D.2. Already today, we can use cabal.project to redirect all D.1 dependencies to D-1-shim (not sure if possible to redirect to Hackage or only to its Git repo?). What’s missing is making this automatic by registering D-1-shim as a pervasive redirect, instead of every user having to discover the solution individually.