I was doing another pass to this idea and checking how the vscode-haskell plugin and ghcup default behaviors with minimal tweaks will work.
I browse vscode-haskell plugin, hie-bios, haskell-language-server and cabal to have a better understanding of the current interaction of the pieces.
The vscode-haskell plugin has some sensible defaults. One should able to set a specific ghc version via cabal.project. If no ghc is specified, the default is to use the recommended. If hls is unspecified, the latest hls version that supports the project required ghc version is picked. If cabal is unspecified the latest cabal is picked.
For this the vscode plugin might install latest ghc, cabal, hls to resolve via hie-bios the expected ghc version and then downgrade the selection (see findHaskellLanguageServer in hlsBinaries.ts).
There is a known issue haskell-language-server#2800 since 2022 that is a stopper for cabal.project to specify the ghc version, unless that specific ghc version is already installed. Related, in cabal#10188 it’s argued that using with-compiler to infer the ghc version is not actually the right way since that is a path which might not reflect the version in it’s name. So if the executable is not found using it as an heuristic for the ghc version is not 100% right. So the with-compiler is not actually a desired way of indicating the ghc compiler version.
Letting the bug aside and issue aside, reading the source code of vscode-haskell plugin I noticed that the default toolchain behavior is true only on fresh installs. If a new version of cabal is released and if the environment is not fresh then the already installed cabal is picked. Similar, if a version happen to be set via ghcup, the default cabal is the one set.
I think we have different treatment on the toolchain at this point:
- By default the
ghcis not pinned, every time the plugin loads it will try to get the recommended ghc.cabal initdoes not produce acabal.projectwith awith-compiler. - If I want to bump
cabalorhlsI need to manually tweak something. Once acabalorhlsis installed that will not be upgraded.
This also means that switching between machines / sharing code can end up with different toolchain setup as it depends on temporal conditions or history. Example:
- Today I have a determined ghc and cabal based on defaults
- Some months later I will get an updated ghc but cabal will still be pinned to the one originally installed.
- On a new machine I get the updated ghc and also a new cabal.
Part of the proposal was creating the notion of “ghc & toolchain project version”. This seems aligned with the concept of the “the ghc of the project” the vscode plugin has. Digging into the vscode-haskell plugin I noticed that such functionality is implemented in haskell-language-server --project-ghc-version. I’m surprised that this logic is not actually part of either cabal or ghcup. After reading hie-bios readme I understand why it shouldn’t be part of cabal. But why not promote that to ghcup?
One of my pain points with “simpler haskell” setup is also to have a single source of truth that can be used in CI. I though that ghcup run would be a way to get around that, but there logic of picking the right tool is in the vscode plugin itself. So we can’t use it in CI. There is no “find the project’s ghc” feature in ghcup. Extracting that from a cabal.project requieres hls/hie-bios and yet is not a desired way to specify the ghc version.
Another example of why all this is a pain point for beginners is that in some CS carrers students might use Haskell in a couple of courses. Unless they become proficient in the tooling or reinstall Haskell at the beginning of the course there si no way the teachers could provide a simple template that will work with a specific version of ghc (As a reminder I do want this to work with ghc and cabal without stack, sorry!).
I think that having a .ghcup file in the project that will specify tool versions will be an improvement:
- The
.ghcupfile can be interpreted byghcup - The vscode plugin will not need to use latest hls to potentially downgrade it’s selection,
ghcupcan provide the expected versions of the tools, the plugin will do the popup validation to install - CI can leverage the same toolchain specification
- It does not use path as in
with-compiler.
It pushes ghcup to be a bit more responsible (or be able to) setup the environment, which according to hie-bios it should be each project to setup the environment.
On the proposal of adding hooks to cabal to address this it still bothers me that we need to through phases to have a complete environment: ghcup, install cabal, use cabal to find ghc. If we have a description we can download and install in parallel.
Related issues/PRs
- Try to find ghc version even if it's not installed by hasufell · Pull Request #2800 · haskell/haskell-language-server · GitHub Try to find ghc version even if it’s not installed by hasufell
- Improve tool finding failure by hasufell · Pull Request #8066 · haskell/cabal · GitHub Improve tool finding failure by hasufell
- Produce a cabal.project file upon `cabal init` to pin the GHC version explicitly · Issue #10188 · haskell/cabal · GitHub Produce a cabal.project file upon cabal init to pin the GHC version explicitly by Kleidukos