A thin layer over the existing Haskell infrastructure in nixpkgs which adds all of the tools needed for interactive development. Here are some of the features that you might find the most useful:
-
An interactive development environment (via
nix-shell
) that includes:-
GHC (8.8.4, 8.10.4, 9.0.1)
-
cabal-install
3.4.0 -
stack
2.7.1 -
hlint 3.3.1
-
haskell-language-server 1.1.0
-
ormolu 0.1.4.1
-
cabal-fmt 0.1.5.1
-
stan 0.0.1.0
-
and a Hoogle database for all of your project's dependencies
NOTE: The following tools do not currently support GHC 9.0.1:
-
haskell-language-server
-
ormolu
-
stan
-
-
Easy to use system for overriding Haskell packages (e.g., use a package not on Hackage, fix a package marked as broken, etc.) without having to write tons of Nix code.
-
Works seamlessly with direnv, lorri, and niv if you already have those tools installed.
-
Switch GHC versions by passing an argument to
nix-build
ornix-shell
. -
Strip dependencies from a package so you can deploy just a binary without tons of other packages coming along for the ride.
-
Create an interactive development environment without adding nix-hs as a project dependency.
-
Fetch pre-built tools from the binary cache.
Create a default.nix
file that looks something like this:
{ pkgs ? import <nixpkgs> { }
}:
let
nix-hs =
import
(fetchTarball
"https://github.com/pjones/nix-hs/archive/release-21.05.tar.gz")
{
inherit pkgs;
};
in
nix-hs {
cabal = ./test/hello-world/hello-world.cabal;
}
And a shell.nix
that looks like this:
# Load an interactive environment:
(import ./. {}).interactive
That's it! Now nix-build
and nix-shell
just work!
In addition to the cabal
argument to the nix-hs
function, there
are other ways to control how your package is built.
If you have a flag defined in your package's cabal file you can enable
it using the flags
argument:
nix-hs {
cabal = ./mypackage.cabal;
flags = [ "someflagname" ];
}
If one of your package's dependencies can't be built you can try overriding it:
nix-hs {
cabal = ./mypackage.cabal;
overrides = lib: self: super: with lib; {
pipes-text = unBreak (dontCheck (doJailbreak super.pipes-text));
};
}
In the example above, the overrides
function takes three arguments:
-
lib
: An attribute set of library functions. These are the functions provided by thepkgs.haskellPackages.lib
set plus a few more that you might find useful such as:unBreak
: Remove thebroken
flag from a packagecompilerName
: The nixpkgs name of the Haskell compiler being used (e.g.ghc884
)pkgs
: The full nixpkgs package set, after overriding
-
self
: The final set of Haskell packages after applying all overrides. This refers to the future version of the package set so if you're not careful you can fall into infinite recursion. When in doubt usesuper
instead. -
super
: The set of Haskell packages that are being modified. Use this attribute set to refer to existing Haskell packages. You can also usesuper
to access useful functions such ascallCabal2nix
andcallHackageDirect
.
The overrides
function should return an attribute set of Haskell
packages. The set of returned packages will be merged into the final
set used to build your package.
An example package using the overrides
function can be found in
test/overrides/default.nix
. The full list of additional lib
functions is in lib/haskell.nix
.
If you have a project that contains multiple Cabal packages you can
build them all with a single default.nix
. The cabal
argument to
nix-hs
can either be a path to a Cabal file or an attribute set of
Cabal files:
nix-hs {
cabal = {
package1 = ./package1/package1.cabal;
package2 = ./package1/package2.cabal;
};
}
The best way to let your text editor and shell use the environment
created from Nix is to use direnv. Here's an example .envrc
file:
# Use lorri if it's installed, otherwise load shell.nix:
if has lorri; then
eval "$(lorri direnv)"
else
use nix
fi
# Reload if these files change:
watch_file $(find . -name '*.cabal' -o -name '*.nix')
NOTE: Make sure you have a shell.nix
file that exposes the
interactive
attribute of the derivation, like the example above.
If you don't want to use nix-hs
to control your default.nix
you
can still use it for building an interactive development environment.
Just clone this repository and use the shell/default.nix
file.
For example, to drop into an interactive shell:
$ nix-shell /path/to/nix-hs/shell
Or
$ nix-shell --argstr compiler 8.8.3 /path/to/nix-hs/shell
Even better, use direnv so your normal shell and text editor can
see all the installed development tools. Here's an example .envrc
file:
use nix /path/to/nix-hs/shell
The derivation generated by the nix-hs
function makes it easy to
access a "binary only" derivation. This is perfect for deployments or
Docker containers where you don't want to bring along all of your
package's dependencies (including GHC).
The bin
attribute of the derivation gives you access to this binary
only derivation. For example, to create a docker container put the
following in docker.nix
:
{ pkgs ? import <nixpkgs> { }
}:
let
mypackage = (import ./. { inherit pkgs; }).bin;
in pkgs.dockerTools.buildImage {
name = "mypackage";
tag = "latest";
config = {
Cmd = [ "${mypackage}/bin/hello" ];
};
}
If you don't want to spend all day compiling the tools needed to build
your Haskell package and its development environment you can use the
nix-hs
cache on Cachix.
The cache is populated after each git push
via a GitHub action.
NOTE: Due to disk space limitations the cache is limited to the current LTS version of GHC.