What is the difference between Cabal and Stack?

2020-02-16 07:00发布

问题:

Yesterday I learnt about a new Haskell tool called Stack. At the first blush, it looks like it does much the same job as Cabal. So, what is the difference between them? Is stack a replacement for Cabal? In which cases should I use Stack instead of Cabal? What can Stack do that Cabal can't?

回答1:

Is stack a replacement for Cabal?

Yes and No.

In which cases should I use Stack instead of Cabal? What can Stack do that Cabal can't?

Since Stack uses the curated stackage packages by default, the packages are known to build together. In Cabal, there is a chance that you may get hit with cabal hell. Stack also locally caches your package, so that you don't compile everything from scratch when you use that package (or its transitive dependency) next time. Note that there is also provision for using non stackage packages, so you are good to go even if a package isn't present in the stackage snapshot.

Personally, I like Stack and would recommend every Haskell developers to use it. Their development is fast. They don't worry (that much) about backwards compatibility. And it has a much better UX. And there are things which stack does which Cabal yet doesn't provide:

  • Stack even downloads GHC for you and keeps it in an isolated location.
  • Docker support (which is very convenient for deploying your Haskell applications)
  • Reproducible Haskell script: You can pinpoint version of a package and can get guarantee that it will always execute without any problem.
  • Ability to do stack build --fast --file-watch. This will automatically rebuild if you change the local files present. Using it along with --pedantic option is a deal-breaker for me.
  • Ability to specify external git repository as an dependency. Starting with Cabal 2.4, cabal also supports external git repository as an dependency. ( A point to note here is that Stack had this feature for more than 3 years and Cabal finally caught up with it).
  • Stack supports creating projects using templates. It also supports your own custom templates.
  • Stack has built-in hpack support in it. It provides an alternative (IMO, a better) way of writing cabal files using yaml file which is more widely used in the industry.
  • Intero has a smooth experience when working with Stack.

There is a nice blog post explaining the difference: Why is Stack not Cabal?



回答2:

In what follows, I will refer to the two tools being compared as cabal-install and stack. In particular, I will use cabal-install to avoid confusion with the Cabal library, which is common infrastructure used by both tools. Broadly speaking, we can say cabal-install and stack are frontends to Cabal, and the key differences between them boil down to what the default workflows are:

  • By default, cabal-install will, when asked to build a project, look at the dependencies specified in its .cabal file and use a dependency solver to figure out (if possible) a set of packages and package versions that satisfy it. This set is drawn from Hackage as a whole -- all packages and all versions, past and present. Once a feasible build plan is found, the chosen version of the dependencies will, by default, be installed and registered at a single database of installed packages somewhere in ~/.cabal.

  • stack, on the other hand, will first look at the resolver field of stack.yaml. That field usually specifies a Stackage snapshot, which is a subset of Hackage packages with fixed versions that are known to be mutually compatible. stack will then, by default, attempt to satisfy the dependencies using exclusively what is provided by the snapshot. Packages installed from one snapshot are registered in different and isolated databases, and separate installations of GHC are maintained to account for what the snapshots require. This approach trades a little flexibility by the guarantee that there will be no version incompatibilities between installed packages (a common problem when using a single package database with cabal-install, for reasons discussed in this article), as well as that it is always possible to figure out the exact versions of dependencies to be used to build a project (which is useful both for ensuring reproducible builds of fully-fleshed projects and for easily specifying dependencies of self-contained .hs scripts without an companion .cabal file).

Do keep in mind that the description above covers the default workflows for each tool. Most of what stack can do is achievable in some (possibly less convenient) manner with cabal-install by stepping out of the defaults, and vice-versa. In particular:

  • cabal-install supports isolated package databases for each project through the cabal sandbox command, though unlike with stack and Stackage snapshots there is no possibility of sharing installed packages across projects. Fixing the versions of dependencies to be used for builds independently of the .cabal is also possible, through cabal freeze. (One related stack feature with no cabal-install analogue is management of separate GHC installations i.e. stack setup.)

  • stack projects can use packages not available from the Stackage snapshot being used by setting the appropriate fields in stack.yaml (extra-deps for packages available from Hackage but not from Stackage, and packages with a custom location for packages not in Hackage). These non-Stackage packages are installed on a per-project basis, with isolation from the snapshots. There is also a stack solver command, which performs automatic dependency solving for Hackage (i.e. non-Stackage) dependencies.

On a final note, it is worth mentioning that support for Nix-style local builds is being added to cabal-install as an alternative way of mitigating version conflicts which is potentially more convenient than cabal sandbox. This feature is available, as a preview release, from cabal-install 1.24.



回答3:

From what I can glean from the FAQ, it seems that Stack uses the Cabal library, but not the cabal.exe binary (more correctly known as cabal-install). It looks like the aim of the project is automatic sandboxing and avoidance of dependency hell.

In other words, it uses the same Cabal package structure, it just provides a different front-end for managing this stuff. (I think!)