Using different versions of the same dependency like Rust

Yeah, that sounds like it could work. We can also copy cargo’s feature of renaming packages which solves the package imports problem. Imagined syntax:

  build-deps: base
            , http-types ^>= 0.12.5 as old-http-types
            , http-types ^>= 1.0.0  as new-http-types
import "old-http-types" Network.HTTP.Types as Old
import "new-http-types" Network.HTTP.Types as New
5 Likes

That’s a really interesting idea I wasn’t aware of. :thinking: I’ll take a look at that in Cargo.

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.

1 Like

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

3 Likes

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.

1 Like

As far as I understand, a lot of these problems (and many other problems) could be solved with a proper module system, right?

module X (
  module Y as YVersion2
  )  where

import Y qualified as Y  -- Pavkage version 2 of package with module Y in scope

Or at least, a proper module system could be part of the solution.

1 Like

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”.

1 Like

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.

1 Like

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.

I’m not convinced this is a good idea:

  • 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.

3 Likes

I agree a better module system could be an alternative, but I don’t quite understand your code example. Can you explain it in a bit more detail?