[ANN] Cabal-3.12.0.0 released!

Is HLS prepared to work with this version of cabal? I really missed this feature. Congrats!

2 Likes

Amazing work!

Unless I’m missing something, it looks like this isn’t listed in the changelog.

1 Like

It’s listed in the other changelog, so you are getting it in the unofficial cabal-install pre-release and in cabal-install 3.12.1.0 once it’s out.

3 Likes

For reference, I did a little measurement, using hyperfine with different versions of GHC, using -j and --semaphore, building the Servant monorepo on a 12-cores machine:

$ hyperfine --prepare "cabal clean" "cabal build --with-compiler ghc-9.6 -O1 -j all" "cabal build --with-compiler ghc-9.8 -O1 -j all" "cabal build --with-compiler ghc-9.8 -O1 --semaphore all"

Benchmark 1: cabal build --with-compiler ghc-9.6 -O1 -j all
  Time (mean ± σ):     84.771 s ±  1.139 s    [User: 597.304 s, System: 92.978 s]
  Range (min … max):   83.351 s … 87.272 s    10 runs
 
Benchmark 2: cabal build --with-compiler ghc-9.8 -O1 -j all
  Time (mean ± σ):     40.627 s ±  0.558 s    [User: 226.383 s, System: 58.612 s]
  Range (min … max):   39.931 s … 41.884 s    10 runs
 
Benchmark 3: cabal build --with-compiler ghc-9.8 -O1 --semaphore all
  Time (mean ± σ):     39.953 s ±  0.195 s    [User: 225.265 s, System: 58.264 s]
  Range (min … max):   39.578 s … 40.235 s    10 runs
 
Summary
  cabal build --with-compiler ghc-9.8 -O1 --semaphore all ran
    1.02 ± 0.01 times faster than cabal build --with-compiler ghc-9.8 -O1 -j all
    2.12 ± 0.03 times faster than cabal build --with-compiler ghc-9.6 -O1 -j all

No extraordinary perf gains on this codebase with --semaphore, but I’m interested in other people’s benchmark results!

5 Likes

To make the prereleases properly accessible to GHCup users, the release team should:

6 Likes

HLS is prepared for cabal’s multi-repl feature. There are some small caveats, though:

  • HLS 2.8.0.0 (not available in ghcup yet, iirc) supports the multi-repl feature but doesn’t use it by default. You have to enable it by explicitly setting the LSP option "sessionLoading": "multiComponent". How you specify this option depends on your LSP client. The respective vscode-haskell release that adds support for this option is yet to happen, as we are bogged down by GitHub CI issues.

  • HLS 2.7.0.0 also supports the mutli-repl feature already, and will use it by default if cabal-install 3.12 is installed on your system and you load more than one component. In other words, you use the experimental multi-repl feature if you load a library and executable into the HLS session and cabal-install 3.12 is on the $PATH.

8 Likes

Congratulations and thank you to everyone involved!

I personally think the multiple home units support will be a big boost for the scalability of Haskell codebases. There are many benefits to splitting projects up into multiple packages, both for the project itself but also for the sake of the ecosystem.

I’ve worked on multiple projects in the past where we wanted to open-source a lot of useful functionality, but we were reluctant to do so because that would’ve required us to extract parts of the mono-package that are actively developed in tandem with downstream modules that depend on the. But without multiple home units support, splitting up packages like that is a recipe for disaster for fast development iterations and IDE support. So, I’m really excited by this development, not only for my own convenience, but also due to how I think it will benefit the ecosystem.

7 Likes

The 3.12.0.0-prerelease cabal binary reports

$ cabal --version
cabal-install version 3.11.0.0
compiled using version 3.12.0.0 of the Cabal library

so that will probably not trigger the multi-repl feature with HLS 2.7.0.0, right? The proper 3.12.1.0 release should, though (and HLS 2.8.0.0 doesn’t care).

Mostly we switched 2.8 to require explicit opt-in as the feature is still new, and might have some issues to shake out. In future we intend to make it the default (when you have tool versions that support it). But please do try it out and give us feedback!

1 Like

I think this should use the multi-repl feature as well, we require the cabal-install version to be >= 3.11, so it will trigger the multi-repl feature with HLS 2.7.0.0

1 Like

You’re absolutely right. Starting with the next one, all pre-releases will be properly moved to downloads.haskell.org and will use the pre-release channel, which is also a nice opportunity for users to discover GHC versions. Thanks for raising this.

But without multiple home units support, splitting up packages like that is a recipe for disaster for fast development iterations and IDE support

I understand the usefulness of multiple home units in the repl, but here I’m using haskell-language-server 2.6.0 on a multi-package project and it seems to work well. Changes in one package are reflected when I go edit another one.

So what will multiple home units add on top of that? What are their advantages for packages with multiple libraries, or projects with multiple packages?

2 Likes

It works fine for small projects. You don’t notice the fact that whenever you change anything in package a all the dependent modules in a need to be recompiled before any module in package b can be reloaded.

Now think about a project with 300 modules all happily living in a single cabal stanza. You’re working on module B and module A in tandem, where module B imports module A. Any time you change anything in A you reload and only module A and then module B get reloaded. Then you split this project into two packages and A ends up in package a with 150 other modules, many of which transitively depend on A (but module B doesn’t depend on them). Now if you change anything in A, you need to wait for all the dependents of A in a to get recompiled before you can reload B.

The situation I describe might sound contrived, but it actually happens a lot with modules like Utils. It also happens a lot when you’re working on the tests in a very large project.

3 Likes

At my job we have 1000s of modules and actually default to having ghci not load any of them - you explicitly :load and it only builds what you need.

Being able to have this workflow across package boundaries will definitely be big for us!

1 Like

Any time you change anything in A you reload and only module A and then module B get reloaded.

Are you referring to reloading in ghci / cabal repl, or to having feedback from HLS after a change? Because, in your example, HLS would eventually recompile the 150 other modules, wouldn’t it?

In my opinion, the most important benefit of multi-repl is correctness and reliability. While HLS works reasonably well, no one can disagree that HLS crashes sometimes. Sometimes HLS reports lovely error messages such as Overlapping instances and reports the same location twice. Sometimes HLS fails with interface file errors. The only way to fix these kinds of errors is restarting HLS, sometimes clearing out the caches.

Many of these hard-errors happen because the support for loading multiple components without cabal’s multi-repl feature (like a library, executable, test suites, sub-libraries, etc…) is simply a hack. A pretty good one, and it served us well for many years by now, but a hack nonetheless.

The multi-repl feature will improve the correctness and reliability of HLS, once its infancy issues have been fixed.

12 Likes

I’ve got a confirmation it works simultaneously across components and across packages, as long as all are “local”, that is, covered in your cabal.project.

2 Likes

I was primarily talking about ghci, because I can see what exactly is happening there. As @fendor has noted HLS seems to have quite a few tricks up its sleeve and unlike him I’m not familiar with them, but I also did notice that HLS has gotten better at handling these cross-package scenarios surprisingly well over the years, and thanks to his answer, many mysterious crashes make much more sense now.

That said, when I say ghci, it’s also ghcid, because I also like to build various workflows on ghcid --test ... when the :reload cycle is fast.

2 Likes

If you want to see the carnage, go look at the issues labelled multi-component on the HLS repo :joy: I’m hoping we can just close most of those…

6 Likes

I have a large multi-library project and I continually get HLS errors about colliding instances because it seems to load both the source and previous compiled versions of the libraries. It’s private code so it would be some effort to report a proper bug, I’m just hoping that this does fix it for me :slight_smile:

3 Likes