James Earl Douglas
July 14, 2015
Development in Haskell is tricky*.
Nix makes it less so. Let's explore how.
Nix is a purely functional package manager.
For us, Nix deconflicts Haskell tools and library dependencies.
Each Nix package is stored in a unique, read-only directory.
For example, GHC 7.10.1:
/nix/store/4w0prjr10rkk096vi9j6s0dcrwggxql3-ghc-7.10.1/
The hash is specific not only to the package, but to its entire dependency graph.
On my system, I have several versions of ghc installed:
/nix/store/4w0prjr10rkk096vi9j6s0dcrwggxql3-ghc-7.10.1/
/nix/store/bc84hdk730zcdm1c9s5yf2ahpvm51lzp-ghc-7.10.1/
/nix/store/d75bw0nllgfblrwrbrs3ppwhmgp8x75k-ghc-7.10.1/
/nix/store/qwy2yv3mvicpr4shmbhlj3yx9il2a9cz-ghc-7.6.3/
/nix/store/vh1ddjjmyb1njxvr5f10apgl51icjy00-ghc-7.8.4/
I have GHC 7.6.3, GHC 7.8.4, and three representations of GHC 7.10.1.
Using a Nix package involves a bunch of symlink Voodoo.
This is mostly handled for you, but there are catches.
/bin/bash /usr/bin/head
In NixOS, even bash
lives in a Nix package.
nope.sh:
yep.sh:
Let's install Nix on an Ubuntu system.
$ curl https://nixos.org/nix/install | sh
This will run some commands as root via
sudo
.
~/.profile:
. /home/james/.nix-profile/etc/profile.d/nix.sh
In my environment, I copied this to ~/.bash_aliases.
Now that Nix is installed, let's build some Haskell!
We'll explore four approaches: two with Cabal, and two without.
Let's start by using Cabal in the standard way.
We'll install Cabal (and GHC) with Nix.
Install the latest versions of cabal-install and ghc into the current environment:
$ nix-env -f "<nixpkgs>" -iA haskellPackages.cabal-install
$ nix-env -f "<nixpkgs>" -iA haskellPackages.ghc
We can now use Cabal right from our shell.
Grab a Haskell project:
$ wget -r -np -nH --cut-dirs=3 -P haq \
https://earldouglas.com/talks/nix-maybe-cabal/haq
$ cd haq
Build and test it:
$ cabal update
$ cabal sandbox init
$ cabal install -j --only-dependencies --enable-tests
$ cabal test
$ cabal build
nix-env -i
nix-env -i
puts a single version of a package in our
environment.
nix-env -e
removes it from our environment.
This is nice and simple, but can get tedious.
With nix-shell, we can start a shell environment that makes specific packages available.
GHC 7.10.1:
$ nix-shell -p haskellPackages.cabal-install haskellPackages.ghc
[nix-shell:~]$ cabal -V
cabal-install version 1.22.4.0
[nix-shell:~]$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.10.1
GHC 7.8.4:
$ nix-shell -p haskellPackages.cabal-install haskell.packages.ghc784.ghc
[nix-shell:~]$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.8.4
Now we can run through the same Cabal-based steps as before.
Nix uses its own language to configure builds via .nix files.
I haven't needed to learn it yet:
// This space intentionally left blank
Include a Nix expressions with nix-shell:
$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [hspec])"
Run ghc, ghci, runhaskell, etc. directly:
[nix-shell:~/haq]$ runhaskell -isrc -itest test/HSpecTests.hs
Validate haqify function
haqify is supposed to prefix Haq! to things
Finished in 0.0065 seconds
1 example, 0 failures
Convert a Cabal configuration to a Nix configuration with
cabal2nix
:
$ nix-shell -p cabal2nix
[nix-shell:~/haq]$ cabal2nix --shell haq.cabal > shell.nix
Use our shell.nix configuration automatically in nix-shell:
$ nix-shell
Run ghc
, ghci
, runhaskell
,
etc. directly:
[nix-shell:~/haq]$ runhaskell -isrc -itest test/HSpecTests.hs
Validate haqify function
haqify is supposed to prefix Haq! to things
Finished in 0.0065 seconds
1 example, 0 failures