I recently learned of Nix, which bills itself as a “purely functional package manager.” It tries to keep things as self-contained as possible, installing each package into a separate directory in its store. Nix is the basis for a Linux distribution called NixOS, but can also be installed standalone.
Nix has great benefits for Haskell development. Due to its insistence on isolation and purity, different versions of packages can be swapped in and out as needed, neatly avoiding “Cabal hell” and the need for sandboxes. As much as possible, the package manager reuses compiled versions of dependencies, greatly speeding up the process after the first time.
One of the great motivations for using Nix to develop Haskell was the ease with which I can swap in profiling libraries (see this blog post for info). Another is the availability of binary caches for many of the other packages on which Haskell packages depend.
Cabal itself might eventually adopt the kind of hermetic builds Nix offers; however, Nix works quite well already for this purpose.
I found Pavel Kogan’s post on Haskell development with Nix to be quite instructive. In this post, I hope to provide some tips for using Nix for Haskell development, specifically on Mac OS X 10.10 (Yosemite).
Getting Started with Nix on Yosemite
Nix does require you to have Xcode’s Command Line Tools installed, if you haven’t done so already.
For now (as of December 2014), you have to use a Nix testing branch in order for it to install on Yosemite.
An article on the Nix wiki details the instructions,
which I’ve reproduced with a few tweaks and comments here:
UPDATE: 2015-05-05 A few months ago, Nix master was updated so that the testing branch is no longer needed; also, the next generation Haskell packages were added to Nix, changing the way things work significantly. In general, despite a few hiccups, the process is already much smoother! I have updated the instructions accordingly, but be sure to check the Nix wiki for the latest info.
curl https://nixos.org/nix/install | sh
NIXDIR=~/nixtest # set this to wherever you want nixpkgs (package definition files) to reside
mkdir -p $NIXDIR
cd $NIXDIR
git clone https://github.com/NixOS/nixpkgs.git
nix-channel --remove nixpkgs
cd ~/.nix-defexpr
rm -rf *
ln -s "$NIXDIR/nixpkgs" .
echo "export NIX_PATH=$NIXDIR/nixpkgs:nixpkgs=$NIXDIR/nixpkgs" >> ~/.bashrc
# create nix configuration dir and add the binary cache for nixpkgs
sudo mkdir /etc/nix
echo "binary-caches = http://zalora-public-nix-cache.s3-website-ap-southeast-1.amazonaws.com/ http://cache.nixos.org/" | sudo tee /etc/nix/nix.conf
source ~/.bashrc # pick up the new NIX_PATH config
# and finally:
nix-env -i nix
This will take a while to install, since it downloads a lot of binaries and does plenty of compiling; if you’re lucky, binary packages will already exist for most of what you need.
Haskell Packages
Once Nix is installed, you’ll want to install cabal2nix, which will grab the other packages you need to work with Haskell:
nix-env -iA nixpkgs.cabal2nix
(You’ll want to go through Nix rather than cabal to install new packages and to build; otherwise, the benefits will be lost.)
The packages on Hackage should be available through Nix. Just type, e.g.
nix-env -iA nixpkgs.haskellngPackages.ghc-mod
nix-env -iA nixpkgs.haskellngPackages.QuickCheck
Adding custom packages
For packages not already in Nix that you want to add, you can set up a
personal config.nix file. First do mkdir -p ~/.nixpkgs/haskell
,
then (for example) to use the version of a package on Git:
cabal2nix --no-check git://github.com/kazu-yamamoto/ghc-mod.git \
> ~/.nixpkgs/haskell/ghc-mod.nix
Finally, make your ~/.nixpkgs/config.nix
file like so:
{ pkgs }: {
haskellPackageOverrides = with pkgs.haskell-ng.lib; self: super: {
zip-archive = dontCheck super.zip-archive;
ghc-mod = pkgs.haskellngPackages.callPackage ./haskell/ghc-mod.nix {};
};
}
The various functions that can be used in this file, such as
dontCheck
(don’t run tests for a package), can be found in your
nixpkgs
folder under pkgs/development/haskell-modules/lib.nix
.
SSL/TLS support
There used to be a problem with using certificates on Mac OS X, but it was fixed!
Building a Haskell app locally
In my case, I wanted to install Hakyll to do static blog generation. Hakyll requires you to write a small Haskell program that defines the structure of your site, which has its own cabal file.
To build your local app, you may want to use nix-shell
. This is
similar to a sandbox, but without requiring everything to be compiled
from scratch. This tool reads a shell.nix
or default.nix
file in
the current directory, and sets up an appropriate environment with all
dependencies at the ready.
To generate shell.nix
, you can use cabal2nix
inside your project
directory containing a .cabal
file:
cabal2nix --sha256="0" --shell . > shell.nix
Once shell.nix
is in place, you can run nix-shell
and have access
to cabal
and any libraries you requested. You can then run cabal build
and the library versions you specified in Nix will be used to
compile.
Finally, if you have any dependencies that are in Nix but not Cabal
(the CSS preprocessor LESS, for instance), you
can add a definition extraLibraries
to your shell.nix
file, like
so:
with import <nixpkgs> {}; [ nodePackages.less ]; extraLibraries =
Here, as an example, is a complete shell.nix file for this Hakyll site. Note that I have added Emacs and ghc-mod to ensure that my editor can pick up the right libraries and give me error-checking capabilities.
with (import <nixpkgs> {}).pkgs;
let pkg = haskellngPackages.callPackage
({ mkDerivation, base, blaze-html, directory, errors, filepath
, hakyll, pandoc, split, stdenv, stm, cabal-install, ghc-mod
}:
{
mkDerivation pname = "corajr";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
buildDepends = [
base blaze-html directory errors filepath hakyll pandoc split stm];
buildTools = [ cabal-install ghc-mod ];
extraLibraries = with import <nixpkgs> {}; [ nodePackages.less emacs24Macport emacs24Packages.structuredHaskellMode ];
license = stdenv.lib.licenses.bsd3;
}) {};
in
pkg.env
Conclusion
Nix has worked great for me thus far: I’ve already been able to resolve several version conflicts that previously would have required sandboxes and a lot of re-installing. Since switching to Emacs as my editor, together with flycheck, I’ve been able to check my code for compile errors on the fly without a hitch. I highly recommend this set-up if you’re looking to do Haskell development.
I’ve done my best to keep up with a lot of exciting developments in this area. Hopefully this post will help others to more readily set up a working environment on Yosemite.
NOTE: I leave the info below about SublimeHaskell for historical interest,
but I don’t know that any of it still applies. If you are a Sublime
Text user wanting to work with Haskell, I definitely suggest that you
give Emacs a try, as its Haskell support is quite good. Do nix-env -iA nixpkgs.emacs24Macport
to acquire the GUI version.
…I still haven’t worked out how to use SublimeHaskell with the Nix-managed set of tools. I’ve thought about trying to use Emacs or another editor that exists within nixpkgs instead, but have yet to set one up. (A version of Sublime is available for NixOS and thus SublimeHaskell ought to work more easily there; perhaps I’ll attempt that in a virtual machine.) If anyone comes up with a solution for this, I’d be glad to hear it.
EDIT: 2014-12-29
I’ve managed finally to set up SublimeHaskell! It required me to use the hsdev
branch to avoid problems with the ModuleInspector.
To install, create Nix expressions for hsdev
and dependency hdocs
, and add them to your ~/.nixpkgs/config.nix:
cabal2nix cabal://hdocs > ~/.nixpkgs/haskell/hdocs.nix
cabal2nix https://github.com/mvoidex/hsdev > ~/.nixpkgs/haskell/hsdev.nix
[...]
[...] {
haskellPackages = hdocs = callPackage ./haskell/hdocs.nix {};
hsdev = callPackage ./haskell/hsdev.nix { inherit hdocs; };
[...]};
Install hsdev
globally with nix-env -iA nixpkgs.haskellPackages.hsdev
. Then, in your project,
create the shell.nix
file as above and add this buildTools
line:
# buildDepends = [...]
[ cabalInstall hsdev ]; buildTools =
Before launching Sublime, create a file "$HOME/Library/Application Support/Sublime Text 3/Packages/User/SublimeHaskell.sublime-settings"
with this contents (replacing “USERNAME” with your own):
{
"add_to_PATH": ["/Users/USERNAME/.nix-profile/bin"],
"enable_hsdev": true
}
Run nix-shell --pure
in the project dir, and open Sublime from that shell: "/Applications/Sublime Text.app/Contents/MacOS/Sublime Text"
Hit Command-Shift-P, type “Add Repository”, and enter: https://github.com/SublimeHaskell/SublimeHaskell/tree/hsdev
Finally, do cmd-shift-p “Install Package” -> SublimeHaskell, and restart the app again.
Running in the Nix shell seems to be required so that hsdev
doesn’t become overwhelmed when setting up autocomplete.
If you open Sublime outside the shell and SublimeHaskell stops working, I suggest to clear the cache by removing the contents
of "~/Library/Application Support/Sublime Text 3/Packages/SublimeHaskell/hsdev"
and starting it again.