NixOS, GHCi, and Mueval

Date
Tags haskell, howto, nixos, programming, tech
Target Audience The intersection of NixOS and Haskell users.
Epistemic Status Was accurate and true at one point, but NixOS does like to change how things are done...

Making lib­raries avail­able in GHCi

Be­cause everything on NixOS is im­mut­able, you can’t just in­stall Haskell pack­ages and have them avail­able in your GHC package data­base. Well, you can, but only in your user­-­level package data­base. You can’t do some­thing like this:

$ cat shell.nix
with im­port <nix­p­kgs> {};
{
  de­moEnv = stden­v.mk­De­riv­a­tion {
    name = "de­mo-env";
    build­In­puts = with pkg­s.haskell­Pack­ages [ ghc text ];
  };
}
$ nix-shell
[nix-shell:~/t­mp]$ ghci
GHCi, ver­sion 8.0.1: ht­tp://www.haskell.or­g/ghc/  :? for help
Pre­lude> im­port Data.­Text

<no loc­a­tion in­fo>: er­ror:
    Could not find module ‘Data.­Text’
    Per­haps you meant
      Data.Set (from con­tain­er­s-0.5.7.1@­con­tain­er­s-0.5.7.1)

You in­stead have to con­struct a GHC whose package data­base has the pack­ages you de­sire. Like so:

$ cat shell.nix
with im­port <nix­p­kgs> {};
{
  de­moEnv = stden­v.mk­De­riv­a­tion {
    name = "de­mo-env";
    build­In­puts = with pkg­s.haskell­Pack­ages; [ (gh­cWith­Pack­ages (p: [p.­tex­t])) ];
  };
}
$ nix-shell
[nix-shell:~/t­mp]$ ghci
GHCi, ver­sion 8.0.1: ht­tp://www.haskell.or­g/ghc/  :? for help
Pre­lude> im­port Data.­Text
Pre­lude Data.­Text>

Making pack­ages avail­able in GHCi and Mueval

This all breaks if you use mueval, as it pulls in the reg­ular GHC. This be­ha­viour seems very odd to me, I’m not sure if it’s a bug or not, but it is how it is:

$ cat shell.nix
with im­port <nix­p­kgs> {};
{
  de­moEnv = stden­v.mk­De­riv­a­tion {
    name = "de­mo-env";
    build­In­puts = with pkg­s.haskell­Pack­ages; [ mueval (gh­cWith­Pack­ages (p: [p.­tex­t])) ];
  };
}
$ nix-shell
[nix-shell:~/t­mp]$ ghci
GHCi, ver­sion 8.0.1: ht­tp://www.haskell.or­g/ghc/  :? for help
Pre­lude> im­port Data.­Text

<no loc­a­tion in­fo>: er­ror:
    Could not find module ‘Data.­Text’
    Per­haps you meant
      Data.Set (from con­tain­er­s-0.5.7.1@­con­tain­er­s-0.5.7.1)

In­stead you have to over­ride the ghc that hint, one of mueval’s de­pend­en­cies, pulls in:

$ cat shell.nix
with im­port <nix­p­kgs> {};
let
  ex­tra­HaskellLibs = p:
    # extra lib­raries you want
    [ p.­text ] ++
    # mueval it­self needs these pack­ages
    [ p.mtl p.QuickCheck p.show p.sim­ple-re­flect ];
  ghc'    = haskell­Pack­ages.gh­cWith­Pack­ages ex­tra­HaskellLibs;
  hint'   = haskell­Pack­ages.h­int.over­ride { ghc = ghc'; };
  mueval' = haskell­Pack­ages.muev­al.over­ride { hint = hint'; };
in {
  de­moEnv = stden­v.mk­De­riv­a­tion {
    name = "de­mo-env";
    build­In­puts = [ mueval' ];
  };
}
$ nix-shell
[nix-shell:~/t­mp]$ ghci
GHCi, ver­sion 8.0.1: ht­tp://www.haskell.or­g/ghc/  :? for help
Pre­lude> im­port Data.­Text
Pre­lude Data.­Text>

However, mueval will not be able to see your new pack­ages yet:

[nix-shell:~/t­mp]$ mueval -X­Over­loaded­Strings -m Data.­Text -e 'Data.­Tex­t.Length "hello world"'
<no loc­a­tion in­fo>: er­ror:
    Could not find module ‘Data.­Text’
    Per­haps you meant
      Data.Set (from con­tain­er­s-0.5.7.1@­con­tain­er­s-0.5.7.1)
    Use -v to see a list of the files searched for.

This is be­cause mueval doesn’t know about the package data­base set up by gh­cWith­Pack­ages. For­tu­nately, we can dis­cover where that is:

#! /nix/store/g­ab­jb­kwga2d­h­h­p2wzy­axl83r8hjjfc37-­bash-4.3-p48/bin/­bash -e
ex­port NIX_GH­C="/nix/store/w7s4z1v5y0r8n­bvq23b4xm4ld­dcmar3r-gh­c-8.0.1-with-pack­ages/bin/ghc"
ex­port NIX_GH­CP­KG="/nix/store/w7s4z1v5y0r8n­bvq23b4xm4ld­dcmar3r-gh­c-8.0.1-with-pack­ages/bin/gh­c-pkg"
ex­port NIX_GH­C_­DOCDIR="/nix/store/w7s4z1v5y0r8n­bvq23b4xm4ld­dcmar3r-gh­c-8.0.1-with-pack­ages/share/­doc/gh­c/html"
ex­port NIX_GH­C_LIB­DIR="/nix/store/w7s4z1v5y0r8n­bvq23b4xm4ld­dcmar3r-gh­c-8.0.1-with-pack­ages/lib/gh­c-8.0.1"
exec /nix/store/b0749p1r­jpyvq2wy­w58x6vg­wp­kwx­cm­nn-gh­c-8.0.1/bin/ghci "-B$NIX_GH­C_LIB­DIR" "${ex­tra­Flag­sAr­ray[@]}" "$@"

Ex­port the NIX_GH­C_LIBDIR vari­able and try again:

[nix-shell:~/t­mp]$ ex­port NIX_GH­C_LIB­DIR="/nix/store/w7s4z1v5y0r8n­bvq23b4xm4ld­dcmar3r-gh­c-8.0.1-with-pack­ages/lib/gh­c-8.0.1"

[nix-shell:~/t­mp]$ mueval -X­Over­loaded­Strings -m Data.­Text -e 'Data.­Tex­t.length "hello world"'
11

For a con­crete ex­ample of this, see yukibot’s README and shell.nix.