Files
haskell.nix/docs/tutorials/development.md
2022-08-15 19:05:37 +12:00

212 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Haskell.nix also provides reproducible development environments for
your Haskell projects. These environments can contain not only GHC and
your Haskell package dependencies, but also the required system
libraries and build tools.
Inside the development shell, you can run commands such as `ghc`,
`ghci`, or `cabal newbuild` (`cabal build` on Cabal 3.0),
and they will have all dependencies available.
Every dependency will be cached in your Nix store. If you have set up
Hydra CI, then your team can share pre-built dependencies.
These examples assume that you have created your package set as
described in [Creating Nix builds for your projects](getting-started.md) and
it exists in a file called `default.nix`.
!!! note
Old-style `cabal build` and `stack` builds are not (yet)
supported. For example, `stack` will (by design) download and
rebuild all dependencies, even though they are available in the
shell. However, if you have a Stack project, you can generate the
package set with Haskell.nix, then use `cabal newbuild` to work
on it. Starting Cabal 3.0 `cabal build` will work out of the box, as
new style builds are the default.
## How to get a development shell
If you have a Cabal or Stack project with one or more packages
(i.e. multiple `.cabal` files, not a single package with multiple
components), then you will need a development environment that
contains the _dependencies_ of your packages, but not the packages
themselves. This is what the [`shellFor`][shellFor] function does.
```nix
# shell.nix
let
project = import ./default.nix;
in
project.shellFor {
# ALL of these arguments are optional.
# List of packages from the project you want to work on in
# the shell (default is all the projects local packages).
packages = ps: with ps; [
pkga
pkgb
];
# Builds a Hoogle documentation index of all dependencies,
# and provides a "hoogle" command to search the index.
withHoogle = true;
# Some common tools can be added with the `tools` argument
tools = {
cabal = "3.2.0.0";
hlint = "latest"; # Selects the latest version in the hackage.nix snapshot
haskell-language-server = "latest";
};
# See overlays/tools.nix for more details
# Some you may need to get some other way.
buildInputs = [ (import <nixpkgs> {}).git ];
# Sellect cross compilers to include.
crossPlatforms = ps: with ps; [
ghcjs # Adds support for `js-unknown-ghcjs-cabal build` in the shell
# mingwW64 # Adds support for `x86_64-W64-mingw32-cabal build` in the shell
];
# Prevents cabal from choosing alternate plans, so that
# *all* dependencies are provided by Nix.
exactDeps = true;
}
```
See also: [Haskell.nix Library Reference: `shellFor`][shellFor]
[shellFor]: ../reference/library.md#shellfor
## How to get a local Hoogle index
If you need a local Hoogle for all the dependencies of your project create this file
```nix
# shell-hoogle.nix
let
project = import ./default.nix {};
in
project.shellFor {
packages = ps: [ps.my-package];
withHoogle = true;
}
```
and run `nix-shell shell-hoogle.nix --run "hoogle server --local"`.
This will open a local Hoogle server at `http://127.0.0.1:8080`.
## How to get an ad-hoc development shell including certain packages
This creates a development environment with the given packages
registered in the package database. The `ghcWithPackages` function
operates on a Haskell.nix package set, and accepts an argument that
selects packages from the larger package set.
```nix
# shell.nix
let
haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {};
nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
haskell = nixpkgs.haskell-nix;
in
haskell.haskellPackages.ghcWithPackages (ps: with ps;
[ lens conduit conduit-extra ])
```
If you need a Hoogle documentation index, use `ghcWithHoogle` in place
of `ghcWithPackages`.
## How to get packages from a certain Stackage snapshot
Haskell.nix knows about every released Stackage snapshot. You can use
it to build packages from a given snapshot, without setting up a full
project.
```nix
let
haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {};
nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
haskell = nixpkgs.haskell-nix;
in
haskell.snapshots."lts-13.18".alex.components.exes.alex
```
There are Haskell.nix package sets for every Stackage snaphot under
`haskell.snapshots`.
The alias `haskell.haskellPackages` corresponds to the package set for
a recent LTS Haskell version.
You can use `ghcWithPackages` on any of these package sets to quickly
get a shell with some packages.
!!! warning
The build will not work if your Nixpkgs does not contain the version
of GHC specified in the snapshot. Nixpkgs only carries the
latest version of each recent release series, so many snapshots
can't be built.
## Emacs IDE support
Once you have a development shell, then you can begin configuring
Emacs to use it. The way I do it is:
1. Run [lorri watch](https://github.com/target/lorri) to continuously
build the shell environment and maintain GC roots.
2. Use [emacsdirenv](https://github.com/wbolster/emacs-direnv) to
push the development environment into Emacs.
3. Use [Dante](https://github.com/jyp/dante) for highlighting errors
and auto-completion. You must customize Dante to prevent it from
automatically using `nixshell` or `stack`. Trim `dantemethods` to
just `newbuild` and `bareghci`.
You can also use [`.dirlocals.el`](https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html)
for this. If your project has multiple targets, set `dantetarget`
per-directory.
4. For [`haskellmode`](https://github.com/haskell/haskell-mode)
interactive Haskell, set `haskellprocesstype` to
`cabalnewrepl`.
## Using `nix repl`
It's sometimes useful to load [Haskell.nix][] in the [REPL](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-repl.html) to explore
attrsets and try examples.
```
# example.nix
{ nixpkgs ? <nixpkgs> }:
rec {
haskell = import nixpkgs (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs;
pkgNames = haskell.pkgs.lib.attrNames haskell.haskell-nix.snapshots."lts-13.18";
}
```
Load the example file:
```
$ nix repl
Welcome to Nix 2.10.3. Type :? for help.
nix-repl> :l <nixpkgs>
Added 16938 variables.
nix-repl> :l example.nix
Added 2 variables.
nix-repl> lib.take 5 pkgNames
[ "AC-Angle" "ALUT" "ANum" "Agda" "Allure" ]
nix-repl> :q
```
Now that you have `nix-tools` and are able to import [Haskell.nix][],
you can continue to the next chapter.
[haskell.nix]: https://github.com/input-output-hk/haskell.nix