Nix, plus or minus Cabal

James Earl Douglas

July 14, 2015

About this talk

Development in Haskell is tricky*.

Nix makes it less so. Let's explore how.

* See duckduckgo.com/?q=cabal+hell

About Nix

Nix is a purely functional package manager.

For us, Nix deconflicts Haskell tools and library dependencies.

Package management

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.

All the packages

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.

Package paralysis

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:

#!/bin/bash
echo This does not work in NixOS!

yep.sh:

#!/usr/bin/env bash
echo This works in NixOS!

Getting started

Let's install Nix on an Ubuntu system.

Installation

$ curl https://nixos.org/nix/install | sh

This will run some commands as root via sudo.

Environment

~/.profile:

. /home/james/.nix-profile/etc/profile.d/nix.sh

In my environment, I copied this to ~/.bash_aliases.

Usage

Now that Nix is installed, let's build some Haskell!

We'll explore four approaches: two with Cabal, and two without.

Cabal via Nix

Let's start by using Cabal in the standard way.

We'll install Cabal (and GHC) with Nix.

Package installation

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

Cabal usage

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

Tradeoff of 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.

Cabal via nix-shell

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.

Using Nix to skip Cabal

Nix uses its own language to configure builds via .nix files.

I haven't needed to learn it yet:

// This space intentionally left blank




Libraries via Nix expression

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

Nix configuration generation

Convert a Cabal configuration to a Nix configuration with cabal2nix:

$ nix-shell -p cabal2nix
[nix-shell:~/haq]$ cabal2nix --shell haq.cabal > shell.nix

Nix configuration usage

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

References

Nix

Haskell and Nix

Haskell