Nix Haskell Development (2020)

Nice post! The wiki would really benefit from this information.

There are only a few things I would add here:

  1. Whenever pinning nixpkgs, I’d recommend adding a comment with the date you used to pin nixpkgs. It is sometimes non-trivial to figure out the date of the channel given just a commit hash, so it is often convenient to know what date a given channel commit corresponds to.

  2. In general, you don’t need to do something like the following:

    (hself: hsuper: {
            ghc = hsuper.ghc // { withPackages = hsuper.ghc.withHoogle; };
            ghcWithPackages = hself.ghc.withPackages;
    })
    

    Instead, I’d recommend most people use shellFor. It works for projects with multiple packages. You can also pass nativeBuildInputs directly to shellFor. (ghcid should technically be in nativeBuildInputs, but in this case it doesn’t really matter.)

    There is a withHoogle argument on shellFor.

    (edit: I originally suggested nativeBuildInput instead of nativeBuildInputs, but that was a mistake. The correct spelling is nativeBuildInputs (plural).)

  3. There are a lot of examples of Haskell-related nix code that take compiler as a argument, giving the ability to compile with multiple versions of GHC.

    In practice, I rarely see people actually use this. Since nixpkgs only contains packages in a single stackage version, there are a whole bunch of packages that don’t compile with different compiler versions. I’d recommend taking this out, just to simplify it somewhat. (Of course, this is a very minor nitpick.)

  4. Unless you have a good reason, I’d recommend against overwriting the top-level haskellPackages attribute in an overlay. Instead, I’d just give it a separate name, like myHaskellPackages. Then you can just refer to it as nixpkgs.myHaskellPackages.

    This can reduce confusion if you need to get development tools (like ghcid) from haskellPackages. Or even if you need to pull in some system library that has a Haskell dependency.

  5. For package overrides, I’d strongly recommend against using cabal2nix to generate Nix expressions on the command line. I consider this an anti-pattern.

    Instead, you should be using callHackage, callHackageDirect, or just callCabal2nix after getting the source with fetchFromGithub.

    The only time you should be using cabal2nix to generate expressions is when your CI doesn’t allow IFD. Although, in that case, I’d recommend just getting a better CI.

  6. It is possible to disable tests, haddocks, profiling, etc for all derivations all at once. To do this, you can override mkDerivation:

    myHaskellPackages = super.haskellPackages.override {
      overrides = hself: hsuper: rec {
        mkDerivation = args: hsuper.mkDerivation (args // {
          doCheck = false;
          doHaddock = false;
          enableLibraryProfiling = false;
          enableExecutableProfiling = false;
          jailbreak = true;
        })
      };
    };
    

    This is nice if you are testing things and frequently rebuilding all Haskell packages, but you want everything to build faster.

    The big downside of this is that you will no longer be able to get any Haskell packages from the shared nix cache.

  7. Link to haskell.nix. Most non-trivial projects are now using haskell.nix.

    haskell.nix has a couple annoying points, but it seems like it will be the way forward for doing Haskell development using nix.


Also, if you end up adding this info to the Haskell wiki page, you may want to link to it from the Haskell reddit. It seems like there is some interest in Nix from the larger Haskell community.

17 Likes