This will soon be an obsolete setting for the next stack release. GHCup will instead use the new stack installation hooks feature, so you get automatic GHC installation, but it’s carried out via ghcup, instead of stack… quite exciting.
Does that mean that in the future the user doing stack build
from within a project will get stack to automatically use the matching GHC version managed by GHCup?
Yes, if they use the hooks feature. So there won’t be duplicated GHC installations. Everything is in ghcup.
It makes uncomfortable reading for me nonetheless, as does the idea that commenters here should be “reproached” for writing something that is easy to interpret in a variety of ways, some of them incorrect, some of them correct.
Stack’s Nix integration works reasonable well for what it is.
In my opinion, here is what it gives you:
- A relatively straight-forward way to get GHC and system libraries from Nixpkgs, while stack downloads and manages Haskell dependencies. (Just like how stack gives you fully reproducible Haskell dependencies, Nix+Nixpkgs can give you fully reproducible system dependencies.)
- A way for users on NixOS to easily build and hack on stack-based Haskell projects.
There are certainly some quirks (including things that are surprising for people who are comfortable with Nix, and a whole other set of things that are surprising for people who are comfortable with Haskell). But it is still usable, and could potentially be a good choice for people on NixOS.
I use it for some of my own projects. I’m reasonably happy with it (especially with how simple it is).
I have been a user of both cabal
and stack
, and I’d love to see a “best of both worlds” user experience, however that is achieved under the hood.
My own journey has looked something like this:
- I used cabal v1 when that’s all there was
- I used cabal v1 with sandboxes to avoid “cabal hell”
- At one point, I was using hsenv so I could use multiple versions of GHC
- I eventually started using stack because it provided a better way to avoid cabal hell while simultaneously also using multiple versions of GHC in a very transparent, no-hassle way
- Recently I’ve migrated to cabal v2, although I’m still using stack for some things, such as when I need to use a different version of GHC
- I haven’t yet learned or used GHCup, but it sounds like a good thing, and I’m sure I’ll look into it at some point
In particular, I’d like to see a way to specify a resolver string, such as “lts-18.28” in a Cabal project file, and have it mean the same things that it does in stack:
- Automatically install and use the version of GHC specified by the resolver.
- Automatically default to the package versions specified by the resolver.
- Have a way for the project to override the versions specified by the resolver. Note that this doesn’t just mean adding packages that aren’t in the resolver. It also means being able to override a package that’s already in the resolver with a newer version. This is often necessary because to add a package that’s not in the resolver, it depends on a newer version of a package that’s already in the resolver, so you need to bump the version of that package.
To me, being able to just name the resolver and have everything happen automatically is important. I’ve seen talk of downloading files, converting files to different formats, adding hooks, etc., but to me that wouldn’t really replace stack if it requires a lot of manual effort to set things up, or to switch from one resolver to another.
If cabal can’t or won’t add support for resolvers, then maybe there needs to be a stackage
tool, which would automate using stackage resolvers with cabal-install. The stackage
tool would be much more limited than stack
, in that it wouldn’t try to be its own build system or reimplement what cabal-install already does. It would simply provide the bits that cabal-install lacks, in order to easily use Stackage LTS in the way I have described.
One question: where is FP Complete in all this? I had thought that they were really the creators and maintainers of stack, rather than it being Snoyman’s personal project. Don’t FP Complete’s customers depend on stack? What are they doing now?
Finally, for those concerned that fragmentation is turning off beginners, I’ll offer this: Haskell is not nearly as bad off as C/C++. Stack and cabal-install both use the .cabal
file format to describe a package. The differences between stack and cabal-install are just in how to build large projects and manage versions.
Compare this to C/C++, where there are low-level build tools like make
and ninja
, and there are higher-level tools like automake
and CMake
which wrap these lower-level tools. And then there are dozens of IDEs with their own project file formats, such as Visual Studio project files, XCode project files, etc., not to mention tons of alternative build systems such as Meson, SCons, and many more.
There is no single, widely-accepted format to describe a package written in C/C++. In comparison, we’re doing quite well!
Of what you ask for:
- Automatically install and use the version of GHC specified by the resolver.
Cabal will not install ghc versions. That is not its purpose. There is a design space for hooking into ghcup for this, and perhaps for connecting the “resolver specified” ghc versions to existing in-scope ghcs that can be explored, but that’s a design philosophy difference that I think is sticking around. Anyone who has thoughts on this should open or look for an issue in cabal where this can be hashed out in more detail. I’ll say its not exactly straightforward to iterate to the “right” solution here.
- Automatically default to the package versions specified by the resolver.
In the latest cabal-3.8 (now in pre-release)!
- Have a way for the project to override the versions specified by the resolver. Note that this doesn’t just mean adding packages that aren’t in the resolver. It also means being able to override a package that’s already in the resolver with a newer version. This is often necessary because to add a package that’s not in the resolver, it depends on a newer version of a package that’s already in the resolver, so you need to bump the version of that package.
The first component exists in the 3.8 functionality. The second component is slated as future work for the next release (3.10).
You should add that it is possible for cabal to automatically use the right GHC version if it is already installed with GHCup. That can be done using e.g. with-compiler: ghc-8.10.7
in your cabal.project
file. That also gives an error if the version of GHC is not found on your system.
Personally, I don’t find that workflow much worse than Stack’s, because you don’t have to install compiler versions that often and you still get guarantees that the right compiler is used.
Good point. Also, with ghcup, you can get all such desired version-suffixed versions in your path. That said, existing exported stackage freeze files don’t have such specifications (and nor am I arguing that they should), so there is a bit of manual work to glue the two bits together. I don’t find that work terribly onerous, but perhaps there is a way it could be streamlined.
They do actually, e.g. https://www.stackage.org/lts-19.16/cabal.config has:
with-compiler: ghc-9.0.2
Oh, neat! That simplifies things considerably
What remains is more of a difference in cli experience and default configuration. Those are probably unlikely to change for a while, but are not features per se anyway.
Objection! UX absolutely is a feature!
I’m also a big fan of lts
. It’s the “killer app” in all of this. I can’t tell you how much time and confidence using lts
provides me working with a codebase over longer periods of time.
re FP Complete – They provided critical support to the key maintainer of stack. FPC is investing more and more in Rust (focusing on the positive side of the zero-sum equation).