Home Explore Blog CI



nixpkgs

9th chunk of `doc/languages-frameworks/haskell.section.md`
5b530e96312d8e7818ac5c6e72ad2b1d5b9abd12523245000000000100000fd5
/nix/store/a8hhl54xlzfizrhcf03c1l3f6l9l8qwv-ghc-9.2.4-with-packages/lib/ghc-9.2.4/package.conf.d
    Cabal-3.6.3.0
    array-0.5.4.0
    base-4.16.3.0
    binary-0.8.9.0
    …
    ghc-9.2.4
    …
```

As you can see, the environment contains a GHC which is set up so it finds all
dependencies of `random`. Note that this environment does not mirror
the environment used to build the package, but is intended as a convenient
tool for development and simple debugging. `env` relies on the `ghcWithPackages`
wrapper which automatically injects a pre-populated package-db into every
GHC invocation. In contrast, using `nix-shell -A haskellPackages.random` will
not result in an environment in which the dependencies are in GHCs package
database. Instead, the Haskell builder will pass in all dependencies explicitly
via configure flags.

`env` mirrors the normal derivation environment in one aspect: It does not include
familiar development tools like `cabal-install`, since we rely on plain `Setup.hs`
to build all packages. However, `cabal-install` will work as expected if in
`PATH` (e.g. when installed globally and using a `nix-shell` without `--pure`).
A declarative and pure way of adding arbitrary development tools is provided
via [`shellFor`](#haskell-shellFor).

When using `cabal-install` for dependency resolution you need to be a bit
careful to achieve build purity. `cabal-install` will find and use all
dependencies installed from the packages `env` via Nix, but it will also
consult Hackage to potentially download and compile dependencies if it can’t
find a valid build plan locally. To prevent this you can either never run
`cabal update`, remove the cabal database from your `~/.cabal` folder or run
`cabal` with `--offline`. Note though, that for some usecases `cabal2nix` needs
the local Hackage db.

Often you won't work on a package that is already part of `haskellPackages` or
Hackage, so we first need to write a Nix expression to obtain the development
environment from. Luckily, we can generate one very easily from an already
existing cabal file using `cabal2nix`:

```console
$ ls
my-project.cabal src …
$ cabal2nix ./. > my-project.nix
```

The generated Nix expression evaluates to a function ready to be
`callPackage`-ed. For now, we can add a minimal `default.nix` which does just
that:

```nix
# Retrieve nixpkgs impurely from NIX_PATH for now, you can pin it instead, of course.
{
  pkgs ? import <nixpkgs> { },
}:

# use the nixpkgs default haskell package set
pkgs.haskellPackages.callPackage ./my-project.nix { }
```

Using `nix-build default.nix` we can now build our project, but we can also
enter a shell with all the package's dependencies available using `nix-shell
-A env default.nix`. If you have `cabal-install` installed globally, it'll work
inside the shell as expected.

### shellFor {#haskell-shellFor}

Having to install tools globally is obviously not great, especially if you want
to provide a batteries-included `shell.nix` with your project. Luckily there's a
proper tool for making development environments out of packages' build
environments: `shellFor`, a function exposed by every haskell package set. It
takes the following arguments and returns a derivation which is suitable as a
development environment inside `nix-shell`:

`packages`
: This argument is used to select the packages for which to build the
development environment. This should be a function which takes a haskell package
set and returns a list of packages. `shellFor` will pass the used package set to
this function and include all dependencies of the returned package in the build
environment. This means you can reuse Nix expressions of packages included in
nixpkgs, but also use local Nix expressions like this: `hpkgs: [
(hpkgs.callPackage ./my-project.nix { }) ]`.

`extraDependencies`
: Extra dependencies, in the form of cabal2nix build attributes. An example use
case is when you have Haskell scripts that use libraries that don't occur in
your packages' dependencies. Example: `hpkgs: {libraryHaskellDepends =

Title: Development Environments Continued: Cabal, ShellFor, and Purity
Summary
This section continues discussing Haskell development environments, focusing on using `cabal-install` and maintaining build purity. It emphasizes the importance of preventing `cabal-install` from accessing Hackage to ensure reproducible builds. It introduces `cabal2nix` for generating Nix expressions from cabal files, enabling the creation of development environments for projects not already in `haskellPackages` or Hackage. It also introduces `shellFor` which provides a declarative way to add development tools to a project's build environment, taking `packages` (a function selecting packages) and `extraDependencies` (additional cabal2nix build attributes) as arguments.