Haskell.nix also provides reproducible development environments for your Haskell projects. These environments can contain not only GHC and your Haskell package dependencies, but also the required system libraries and build tools.

Inside the development shell, you can run commands such as ghc, ghci, or cabal new‑build (cabal build on Cabal 3.0), and they will have all dependencies available.

Every dependency will be cached in your Nix store. If you have set up Hydra CI, then your team can share pre-built dependencies.

These examples assume that you have created your package set as described in Creating Nix builds for your projects and it exists in a file called default.nix.

Note:

Old-style cabal build and stack builds are not (yet) supported. For example, stack will (by design) download and rebuild all dependencies, even though they are available in the shell. However, if you have a Stack project, you can generate the package set with Haskell.nix, then use cabal new‑build to work on it. Starting Cabal 3.0 cabal build will work out of the box, as new style builds are the default.

How to get a development shell

If you have a Cabal or Stack project with one or more packages (i.e. multiple .cabal files, not a single package with multiple components), then you will need a development environment that contains the dependencies of your packages, but not the packages themselves. This is what the shellFor function does.

# shell.nix
let
  project = import ./default.nix;
in
  project.shellFor {
    # ALL of these arguments are optional.

    # List of packages from the project you want to work on in
    # the shell (default is all the projects local packages).
    packages = ps: with ps; [
      pkga
      pkgb
    ];

    # Builds a Hoogle documentation index of all dependencies,
    # and provides a "hoogle" command to search the index.
    withHoogle = true;

    # Some common tools can be added with the `tools` argument
    tools = {
      cabal = "3.2.0.0";
      hlint = "latest"; # Selects the latest version in the hackage.nix snapshot
      haskell-language-server = "latest";
    };
    # See overlays/tools.nix for more details

    # Some you may need to get some other way.
    buildInputs = [ (import <nixpkgs> {}).git ];

    # Sellect cross compilers to include.
    crossPlatforms = ps: with ps; [
      ghcjs      # Adds support for `js-unknown-ghcjs-cabal build` in the shell
      # mingwW64 # Adds support for `x86_64-W64-mingw32-cabal build` in the shell
    ];

    # Prevents cabal from choosing alternate plans, so that
    # *all* dependencies are provided by Nix.
    exactDeps = true;
  }

See also: Haskell.nix Library Reference: shellFor

How to get a local Hoogle index

If you need a local Hoogle for all the dependencies of your project create this file

# shell-hoogle.nix
let
  project = import ./default.nix {};
in
  project.shellFor {
      packages = ps: [ps.my-package];
      withHoogle = true;
  }

and run nix-shell shell-hoogle.nix --run "hoogle server --local". This will open a local Hoogle server at http://127.0.0.1:8080.

How to get an ad-hoc development shell including certain packages

This creates a development environment with the given packages registered in the package database. The ghcWithPackages function operates on a Haskell.nix package set, and accepts an argument that selects packages from the larger package set.

# shell.nix
let
  haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {};
  nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
  haskell = nixpkgs.haskell-nix;
in
  haskell.haskellPackages.ghcWithPackages (ps: with ps;
    [ lens conduit conduit-extra ])

If you need a Hoogle documentation index, use ghcWithHoogle in place of ghcWithPackages.

How to get packages from a certain Stackage snapshot

Haskell.nix knows about every released Stackage snapshot. You can use it to build packages from a given snapshot, without setting up a full project.

let
  haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {};
  nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
  haskell = nixpkgs.haskell-nix;
in
  haskell.snapshots."lts-13.18".alex.components.exes.alex

There are Haskell.nix package sets for every Stackage snaphot under haskell.snapshots.

The alias haskell.haskellPackages corresponds to the package set for a recent LTS Haskell version.

You can use ghcWithPackages on any of these package sets to quickly get a shell with some packages.

⚠️ Warning:

The build will not work if your Nixpkgs does not contain the version of GHC specified in the snapshot. Nixpkgs only carries the latest version of each recent release series, so many snapshots can't be built.

Emacs IDE support

Once you have a development shell, then you can begin configuring Emacs to use it. The way I do it is:

  1. Run lorri watch to continuously build the shell environment and maintain GC roots.

  2. Use emacs‑direnv to push the development environment into Emacs.

  3. Use Dante for highlighting errors and auto-completion. You must customize Dante to prevent it from automatically using nix‑shell or stack. Trim dante‑methods to just new‑build and bare‑ghci.

    You can also use .dir‑locals.el for this. If your project has multiple targets, set dante‑target per-directory.

  4. For haskell‑mode interactive Haskell, set haskell‑process‑type to cabal‑new‑repl.

Using nix repl

It's sometimes useful to load Haskell.nix in the REPL to explore attrsets and try examples.

# example.nix
{ nixpkgs ? <nixpkgs> }:
rec {
  haskell = import nixpkgs (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs;
  pkgNames = haskell.pkgs.lib.attrNames haskell.haskell-nix.snapshots."lts-13.18";
}

Load the example file:

$ nix repl
Welcome to Nix 2.10.3. Type :? for help.

nix-repl> :l <nixpkgs>
Added 16938 variables.

nix-repl> :l example.nix
Added 2 variables.

nix-repl> lib.take 5 pkgNames
[ "AC-Angle" "ALUT" "ANum" "Agda" "Allure" ]

nix-repl> :q

Now that you have nix-tools and are able to import Haskell.nix, you can continue to the next chapter.