Stack and Nix
In many cases, Stack and Nix are considered competing toolsFor example, Chris has previously written an article explaining Nix in terms of Stack and how Nix can be considered a replacement for Stack. rather than complementary ones. However, there are circumstances in which you want them to learn to play nice together. If you find yourself in that situation, here are some tips from the trenches, as it were, on using Stack’s Nix integration.
How Stack gets GHC
A Stackage package set specifies not only the particular versions of Haskell packages that work together, but also the specific version of GHC that the packages are known to work with.
For example, if we look at Stackage LTS 13.1, we can see that it uses GHC version 8.6.3.
When you use Stack, it will install the appropriate Haskell compiler automatically. We are not supposed to think too deeply about the internal machinations of Stack and how it manages to get GHC installed, it just does. Unless it doesn’t. If we try to use Stack on NixOS, its default GHC-installation techniques fail:
Fix this by enabling Stack’s Nix integration features.Even when you are not using NixOS, you can use the Nix integration if that is your preference. You must first have the Nix package manager installed. There are various ways to do this:
- By passing Stack the
- In your project’s
- In your user-wide Stack configuration file,
We recommend the last option for NixOS users; Stack will not work at all on NixOS without Nix integration enabled, so you may as well turn it on for all your projects at once.
Create the file
~/.stack/config.yaml if it doesn’t already exist, and add the following lines to it:
With this option enabled, Stack will obtain GHC using the Nix package manager instead of its own methods.
Nix path configuration
As a Nix user, you likely have an environment variable called
NIX_PATH.In a typical Nix setup, you probably use the
nix-channel program to manage the directories that
NIX_PATH refers to.
NIX_PATH is not set up at all, you will see an error like this:
NIX_PATH is sort of analogous to the
PATH environment variable, except whereas
PATH is a list of directories containing programs that you already have installed,
NIX_PATH is a list of directories containing Nix expressions that specify what you can install.
This means that there is some compatibility requirement between your Nix channel and your Stack resolver. If you are using a Stack resolver that requires GHC 8.6.3, then you need to be using a Nix channel that contains a build definition for GHC 8.6.3. If your Stack resolver and Nix channels are out of sync in some way, you will see an error message like this one:
Most often this happens to us when one of the two components has fallen far behind the other.
- When we turn on a dusty old NixOS machine that hasn’t been updated in a while and try to start a new Stack project, the Stack resolver is new and will probably require a version of GHC that isn’t defined in our old Nix channels. Bringing our Nix channels up to date typically solves this problem.
- When we go back to work on an old Haskell project with a
stack.yamlfile that hasn’t been updated in a while, it might specify a Stack resolver that wants a GHC version that no longer exists in newer Nix channels. Modifying the
stack.yamlto use a newerVisit stackage.org/lts to find the version number for the latest Stack package set. resolver typically solves this problem.
The second case occurs because the maintainers of nixpkgs don’t support old versions forever. In particular, nixpkgs tends to quickly remove older minor GHC releases. GHC version numbers have the form “X.Y.Z” where the final component Z is incremented for releases that contain only bug fixes, and so moving from GHC 8.6.3 to GHC 8.6.4 is considered a minor version upgrade.
It isn’t unusual to want an older major release version, because major versions can change functionality; code that compiles with GHC 8.4 might not compile with GHC 8.6. But typically there is no reason to ever continue using a previous minor version, and so nixpkgs feels comfortable dropping support for 8.6.3 once GHC 8.6.4 is available. Stack, however, does not take the perspective that minor releases are equivalent enough to be interchangeable.
Even with Nix integration enabled, Stack will still build all of your project’s Haskell dependencies itself, rather than using the Nix cache. However, if your Haskell dependencies in have non-Haskell dependencies, it can use Nix to obtain them.
Perhaps the most common Haskell package with a native (non-Haskell) dependency is a compression library called zlib. The Haskell package is mostly just a wrapper over the underlying library which is written in C and must be installed separately.
Trying to use a package that depends on
zlib, if you don’t have the C library installed, will result in this error:
To add the native
zlib dependency to the environment in which Stack compiles the
zlib Haskell package, we need to go back to our Stack configuration file,
~/.stack/config.yaml. Under the
nix: section, add a subsection called
packages: is a list of Nix packages. Here, the name of the Nix package we need happens to be the same as the Haskell package, but this is not always the case. Make sure each package you list here matches the name of a Nix package.
You may also list Nix packages in a project’s
stack.yaml file. Your
stack.yaml file might look something like this:
Even if you don’t use Nix yourself, this can be a courteous thing to provide for Nix users who may use Stack to work on your project.