This is a flake template for a Haskell project that aims to provide a high power-to-weight ratio and clear instructions for the most common workflows.
In particular:
-
the provided flake only depends on functionality from Nixpkgs
-
the template covers most Nix tweaks you'll need in practice
-
the flake is short and clear
Run the following commands within a Git checkout of your Haskell package:
$ nix flake init --template github:Gabriella439/haskell-flake
… then edit flake.nix
to replace all occurrences of ${name}
with your
package's name.
Once you've done that then you can enter the Nix shell using nix develop
:
$ nix develop
… and inside that shell you can run code .
to edit your Haskell package using
VSCode, which will already have the right Haskell plugins installed along with
a Haskell language server.
If you use direnv
then instead of using nix develop
you can add this line
to your .envrc
:
use flake .
Tip
If you use direnv
to provide your Nix shell, consider using
nix-direnv
to improve the
experience.
You can depend on a local checkout of one of your dependencies by editing the
packageSourceOverrides
section, like this:
(hlib.packageSourceOverrides {
${name} = ./.;
${dependencyName} = /path/to/dependency;
})
In fact, this is the same way that the flake depends on your current Haskell package.
If you depend on a local checkout then you need to pass the --impure
flag to
the nix develop
command when you enter the Nix shell:
$ nix develop --impure
… or if you use direnv
then edit your .envrc
to use this instead:
use flake . --impure
Nixpkgs supplies a default version for most packages on Hackage out-of-the-box,
but you can depend on new packages or non-default versions of existing packages
by editing the packageSourceOverrides
section, like this:
(hlib.packageSourceOverrides {
${name} = ./.;
# Add your desired package and version here
${hackagePackageName} = ${desiredVersion};
})
For example, if you wanted to depend specifically on version 2.2.3.0 of the
aeson
package, you'd specify:
(hlib.packageSourceOverrides {
${name} = ./.;
aeson = "2.2.3.0";
})
Then run:
$ nix flake lock --update-input all-cabal-hashes
You can depend on a package from GitHub instead of Hackage by running this command:
$ cabal2nix "${GITHUB_URL}" > "./dependencies/${PACKAGE_NAME}.nix"
$ git add "./dependencies/${PACKAGE_NAME}.nix"
For example, if you wanted to depend on turtle
's GitHub repository, you'd run:
$ cabal2nix https://github.com/Gabriella439/turtle > ./dependencies/turtle.nix
$ git add ./dependencies/turtle.nix
By default cabal2nix
will use the latest revision from that package. If you
want to specify an older revision then use the --revision
flag:
$ cabal2nix --revision "${REVISION}" "${GITHUB_URL}" > "./dependencies/${PACKAGE_NAME}.nix"
Technically you can use cabal2nix
and the ./dependencies
directory for
things other than GitHub repositories, but usually for most other types of
dependencies packageSourceOverrides
will be more ergonomic.
You can tweak a package using the overrides section here:
self.lib.composeManyExtensions [
…
(hself: hsuper: {
# Overrides go here
})
];
For example, if you wanted to disable the test suite for the vector
package,
you'd write:
(hself: hsuper: {
vector = hlib.dontCheck hsuper.vector;
})
You can find the full list of available functions here.
If you're wondering why the flake assigns the updated package set to a new
haskellPackagesCustom
attribute:
haskellPackagesCustom = self.haskellPackages.override (old: {
… instead of updating the haskellPackages
attribute in place like this:
haskellPackages = super.haskellPackages.override (old: {
… it's because the latter will trigger an infinite recursion if you use
packageSourceOverrides
to override any dependency of cabal2nix
(and
cabal2nix
has a decent number of dependencies, including aeson
, lens
and
optparse-applicative
). In particular, this happens because
packageSourceOverrides
depends on callCabal2nix
, which in turn depends on
haskellPackages.cabal2nix-unwrapped
, so if you define haskellPackages
in
terms of packageSourceOverrides
you can accidentally trigger an infinite
loop if any of the entries in packageSourceOverrides
affect
haskellPackages.cabal2nix-unwrapped
.
The simplest way to avoid this problem is to not override the
haskellPackages
attribute and to create a new attribute
(haskellPackagesCustom
in this case).