Hacker News new | ask | show | jobs
by bjz_ 2200 days ago
The new improvements to Cabal have been super nice of late, but one thing I _really_ wish Cabal would do is allow for multiple versions of a library to be used in the same project. Having to satisfy a single library version is incredibly frustrating, and the solver errors are incredibly confusing. This is especially confusing when it complains about a library deep in your dependency graph being in conflict.

Ultimately the failure mode means that you can't build your package and are blocked until a fix is made upstream, where as with a package manager like Cargo you can still build your project. The downside is that you have a duplicate in your dependency graph - but this can be fixed upstream in an asynchronous fashion.

3 comments

I agree that this is a pain point. I not familiar with Cargo and would like to know how this actually works. Don't you risk binary incompatibility issues because the same symbols are occupied by different versions of the same package?

For example, what if my package depends on A(v1.0) and B, B depends on A(v2.0), and furthermore B exposes a type from A(v2.0) in its API? Does the package manager distinguish internal dependencies from dependencies that are exposed in the API of the package?

I think Rust gives the symbols unique hashes for each crate version to avoid this. See this answer on Stack Overflow for more information: https://stackoverflow.com/a/51722134 - not sure if there is a better reference document though.

You sometimes get weird errors like expected `A` but found `A` in the unusual event that you actually run into this, but this is probably something that could be fixed.

Thank you for the link, that clarified it for me. It seems that it requires compiler support to do it in the same way as Rust does it, but I like that approach better than complicating the package definitions with two types of dependencies.

Reading that post also reminded me of the Unison language [1] which would even allow the same type from different versions of a library to be identified as the same if it wasn't altered. It does this by identifying every type and function by the hash of their respective definitions.

[1] https://www.unisonweb.org/

What's interesting is that GHC absolutely supports this already. If you manually link your projects you can use multiple versions of the same library in your project. Cabal doesn't support multiple versions of the same library.
You might be interested in package-qualified import, for overlapping types and symbols: https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...
Source Repository stanzas and github's fork button (or similar buttons on other platforms) help me to avoid any blocking coming from upstream packages[1]:

[1] https://www.haskell.org/cabal/users-guide/nix-local-build.ht...

Using bazel as a build tool (on top of cabal) allows you to provide patches or modifications to found versions.
What happens if you are building a library that is published to Hackage?
Yes, you can apply patches from (mostly) any source. There'd be a slightly different markup/setup depending on how you popoulate your stackage/hackage - nix or http_archive (stack_snapshot). If you're modifying something from an upstream it'd be best to reference it as vendored, e.g. `@org//:package` and not the stack populated version `@stack//:package`. This is how you'd do it pulling from git directly (there's a whole lot more involved if this were to be done for a stackage package).

    http_archive(
        name = ...,
        build_file_content = """
          load(
            "@org//bazel:defs.bzl",
            "pai_haskell_library",
          )

          pai_haskell_library(
            name = ...,
            version = ...,
            srcs = glob(["src/**/*.hs"]),
            stackage_deps = [...],
            visibility = ["//visibility:public"]
          )
        """,
        patch_args = ["-p1"],
        patches = [
            "//dir:git-patch-file",
        ],
        sha256 = "...",
        urls = ["https://github.com/org/repo/archive/1.0.0.0.tar.gz"],
    )