Another Haskell and Nix setup

Posted on May 6, 2021

Note: after reading this post, read this Reddit post by a Nixpkgs Haskell maintainer who has many good suggestions for further improvement.

There are already several guides on how to build Haskell programs with Nix out there. However, when I set up Nix-based builds for a Haskell program I maintain, the examples I could find were either too simple, or far too complicated. I’m not very skilled at using Nix, so I needed concrete examples I could understand and modify. Eventually I cobbled together something I’m mostly happy with, which I’m now writing up as the example I would have liked to have found myself. What follows is a superficial walkthrough of the files I use to build tarballs containing binary builds of the Futhark compiler.

Remaining issues

I am mostly happy with this setup, but it does have problems. One is that I link statically with glibc - this is usually considered a bad idea, because glibc’s name lookup service depends on dynamic linking. The program we’re building doesn’t perform any network requests, so this does not matter to us, but it might matter to others.

Another problem is that the program is built against the Haskell packages present in the pinned version of Nixpkgs. This usually corresponds to a recent LTS snapshot from Stackage, which is nice in the sense that everything is internally compatible, but has two major problems:

  1. Stackage LTS can move frustratingly slowly, because they cannot update to new versions until all users have catched up. You might need newer versions than what Nixpkgs contains, in which case you need to use cabal2nix to define a derivation for a newer version, just like I do for versions above.

  2. It is unlikely that the versions of library dependencies included in Nixpkgs will be the exact same versions as you’d get from a stack build or cabal build (whose solvers don’t use Nixpkgs). This means that nix-build will use different dependencies than you will be using during your own development. I have not been bitten by this yet, but I imagine it can result in an interesting debugging experience.

A better solution would be to run the Cabal solver against Hackage and generate a .nix file for exactly those versions that it picks. I don’t know how to do that, but it seems like an obviously good idea, so I expect that cabal2nix supports it somehow.

A supplementary rant

I think putting Haskell libraries in Nixpkgs is a fundamentally bad idea. The philosophy that every Haskell package in Nixpkgs must use the same versions of library dependencies is not scalable, and in practice often means that the futhark derivation in Nixpkgs is broken. This doesn’t affect my own CI builds (I use cabal2nix to supplement Nixpkgs with whatever I need), but it does mean that I cannot in good faith tell NixOS users to install Futhark from their package system - the odds that it will be broken at any given time are far too high. I’m sure Nixpkgs is careful to ensure major Haskell programs such as Pandoc and ShellCheck remain working, usually by including multiple versions of problematic dependencies in ad-hoc ways, but smaller Haskell programs seem to be out of luck. A better solution would be to give every Haskell program its own distinct set of Haskell library dependencies, resolved by Cabal (or Stack).