Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Differences between Haskell tools Stack and Cabal?
Haskell tooling can be confusing. Both Stack
and Cabal
appear to be build tools with similar goals.
How do they differ? Why should you pick one over the other?
1 answer
The short, broad strokes answer is that (modern) cabal-install
uses a nix-style approach where there's a shared global cache, but you can have multiple versions of a package installed or even the same version of a package installed with different versions of its dependencies. stack
takes an approach similar to a typical Linux OS package manager where there's a "blessed" set of versions of packages that are semi-manually verified to work well together.
There are some other smaller or more situational differences. For example, stack
will install GHC binaries, but nowadays you should probably use GHCup for that.
If you don't have strong feelings about either of the above approaches to package management, then, personally, I'd recommend starting with cabal-install
. It seems some tooling may work better out of the box with it, and it's not going anywhere. That said, for simple, basic usage, the experience with either should be more or less the same. Both provide a cmd install some_package
experience developers have come to expect.
Elaborating a bit. First, while I analogized stack
to an OS package manager, unlike such a package manager, you can easily have different project use different "snapshots" of packages, so it's not as all-or-nothing as an OS package manager.
Similarly, while (modern) cabal-install
uses a nix-like approach, it doesn't require the relatively complicated .nix
files of actual nix. Also, unlike many other popular language package managers, e.g. pip
or npm
, cabal-install
has had a fairly sophisticated solver throughout much of its life[1]. This means, modulo the accuracy of the version bounds provided by libraries, it can provide a working build plan if one exists.
Some historical context may also be useful.
To start, a standard disclaimer is that "Cabal" refers to multiple aspects of the package management infrastructure for Haskell. It refers to the Cabal framework, the Cabal library, the .cabal
file format, and, of course, to the cabal-install
tool. stack
builds on the underlying Cabal infrastructure, and the main difference is between the stack
and cabal-install
tools.
Cabal goes back to the Library Infrastructure Project in 2003 and its first release in 2005[2]. You can look at the original Library Infrastructure Project proposal to see what other systems they were looking at. The original versions of cabal-install
used a global package store which, while much better than the nothing that preceded it, was pretty disastrous. That said, even very early on, a nix-like approach (Nix being a fairly new and interesting project in this timeframe) was an aspirational goal for cabal-install
. However, what followed was a "sandbox" approach where each of your projects had its own collection of packages. This pattern is (now) very familiar from tools like npm
or Python virtual environments.
It was during this era that Stackage circa 2012 and stack
circa 2015 started. Stackage preceded cabal sandboxes, while stack
followed their existence. Stackage was a response to the "Cabal hell" due to global repositories and limitations of the solver at the time. stack
was created to address UX issues around using Stackage with cabal-install
as well as other missing features of cabal-install
. You can see a retrospective circa 2018 on stack
by one of its original creators here.
It is no secret that this period was marked by quite a lot of acrimony due to "forking" the main build tool. On the one side, many of the issues that stack
was addressing had solutions or had solutions in development when stack
was created, and so it would seem better to avoid duplicating effort and downstream effort and potentially fragmenting the community. On the other hand, it seems clear that some features of stack
were never going to be incorporated into cabal-install
, and even if they were, it would take a long time. There were also some "personality" conflicts that amplified these issues.
In 2019, the nix-style approach, which had been a v2/new-build option for several years, became the default for cabal-install
. While sandboxes and improvements to the solver resolved much of "Cabal hell", the nix-style builds do still do a good job of getting the benefits of sandboxes (different per-project versions of things and isolation) while avoiding many of the downsides (duplicate copies, redundant builds). There are some things sandboxes make nicer, so them being removed entirely from cabal-install
is a bit annoying, though technically you can get a similar experience with a local package environment. There's talk about re-adding a sandbox experience on top of the nix-style builds.
My vague impression is that stack
has lost a lot of the momentum it had in the past, while cabal-install
has gained momentum. stack
had the benefit of being commercially backed from the start, while cabal-install
, like a lot of development in Haskell, had been entirely volunteer-driven. Haskell has gotten a lot more explicit industry support over time which I think has led to cabal-install
development moving faster than it had in the past. As mentioned even in the 2018 retrospective above, cabal-install
has resolved most of the most painful issues that motivated stack
. As I said at the very beginning, the differences in package management philosophy are, in my opinion, the core distinctions which aren't likely to come together anytime soon. Arguably, that's more about using Stackage rather than stack
.
Personally, as someone who's essentially never used Stackage nor stack
, I would appreciate "official" support for Stackage in cabal-install
. I probably still wouldn't use it most of the time, but the "curated" package sets that Stackage provides do solve real problems. Not every incompatibility is going reflected in version bounds or even a successful compile. That said, especially once sandboxes were introduced, I've only rarely had incompatibility issues and many of them were inherent and thus not solvable by using stack
. More broadly, for my purposes limited and out-of-date packages in OS distributions have caused me more frustration than unexpected incompatibilities. Others have different values and experiences.
0 comment threads