Nix Flake a Haskell Project
_ _ _
| \| | (_) __ __
| .` | | | \ \ /
|_|\_| |_| /_\_\
___ _ _
| __| | | __ _ | |__ ___
| _| | | / _` | | / / / -_)
|_| |_| \__,_| |_\_\ \___|
__ _
/ _` |
\__,_|
_ _ _ _ _
| || | __ _ ___ | |__ ___ | | | |
| __ | / _` | (_-< | / / / -_) | | | |
|_||_| \__,_| /__/ |_\_\ \___| |_| |_|
___ _ _
| _ \ _ _ ___ (_) ___ __ | |_
| _/ | '_| / _ \ | | / -_) / _| | _|
|_| |_| \___/ _/ | \___| \__| \__|
|__/
╔─*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*─╗
║1 ........................................ 1║
║2* ........................................ *2║
║3 ........................................ 3║
║1 ...........Posted: 2024-03-12........... 1║
║2* ...........Tags: haskell nix ........... *2║
║3 ........................................ 3║
║1 ........................................ 1║
╚────────────────────────────────────────────────╝
I wrote these notes because I wante to reduce the friction for myself and others
to getting a Nix Flake working for their Haskell Project.
Some of these tips might be useful for Haskell in general.
i recommend only using nix if you have non-haskellpur edepends
* TOC {:toc}
## Why use a Nix Flake?
* Reproducibility.
* nix build
* nix develop
## Resources
https://input-output-hk.github.io/haskell.nix/tutorials/getting-started.html
https://input-output-hk.github.io/haskell.nix/tutorials/getting-started-flakes.html
## Installing nix
https://nixos.org/download#nix-install-linux
Then start a new terminal.
Add this to `~/.config/nix/nix.conf`:
```
experimental-features = nix-command flakes
```
Now add caching if you want to avoid taking forever, to your
`/etc/nix/nix.conf`:
```
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
substituters =
https://cache.nixos.org/ https://cache.iog.io
```
Be sure to pay attention to things it's building when it should just be grabbing
from cache.
Whenever you modify `/etc/nix/nix.conf` you may want to use `sudo systemctl
restart nix-daemon`.
Although, it seems cabal is being built, so I'm not sure if it's actually using
the cache.
## Start setting up your project/flake
We're going to use this:
https://community.flake.parts/haskell-flake/start
I'm going to be going off the example of adding a nix flake to an old-ish
project of mine.
In your project repo/root:
```
nix flake init -t github:srid/haskell-flake
```
Edit the new `flake.nix` to set `packages.default` to something like `
packages.default = *self'*.*packages*.yourproject;` .
### Use a specific GHC
I came back to a project (burrow) that was difficult to compile because I
neglected to pin the GHC version and dependency versions or whatever. My goal
was to modify the project as little as possible while introducing nix to build
it and making compilation in general more stable for longevity and other users.
My project used:
```
base >=4.7 && <5,
```
And Here's a list where you can tell which version of GHC you may want to use
based off your `base` constraints:
https://wiki.haskell.org/Base_package
This tells me it can use anything from 7.8.1 to (as of now) 9.8.1. Based off of
deduction of APIs I kenw I had to set the versions of a few dependencies in my
cabal file, basically, too. However, one of the dependencies (`errata`) I had
to pin wanted something between `4.12` (GHC 8.6.1) and `4.17` (GHC 9.4.1). I
changed my `base` constraints around this in the hopes it'd make things easier.
Things to pay attention to in the `flake.nix` generated:
* Is the cache being used?
* `nixpkgs.url`: this may come in handy:
https://lazamar.co.uk/nix-versions/
* `basePackages`
I also found out a package I was using is broken in nixpkgs right now, so I just
added it myself and removed it from dependencies (a library to convert a number
to a roman numeral).
Another issue I encountered was I needed a specific version of errata,
basically, based off the API.
I also searched nixpkgs to see which version holds errata 0.3.0.0 so I could set
`nixpkgs.url` basically, luckily the flake made this easy:
```
packages = {
errata.source = "0.3.0.0"; # Hackage version override
# shower.source = inputs.shower;
};
```
after searching for the right errata version in
https://lazamar.co.uk/nix-versions/ -- I found it in nixos-22.11. After doing so
I think it was clear that I should use something older and more stable than the
nixpkgs `unstable`, so I did this:
```
nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11";
```
Even so I still have this left over:
```
hspec-golden >=0.1 && <0.2
```
Which I found strange because of these results:
https://lazamar.co.uk/nix-versions/?channel=nixos-22.11&package=hspec-golden
So i just made sure to set to the latest version that comes before 0.2:
```
packages = {
errata.source = "0.3.0.0"; # Hackage version override
hspec-golden.source = "0.1.0.3";
};
```
I then got a complaint about missing `word-wrap ==0.4.1`, which was in
`nixos-22.11`, so I did the same basically:
```
packages = {
errata.source = "0.3.0.0"; # Hackage version override
hspec-golden.source = "0.1.0.3";
word-wrap.source = "0.4.1";
};
```
Here's where I learned that `0.4.1.0` does not equal `0.4.1` to nix or
something... anyway after this the project built!
I made sure that there was a `flake.lock` and I also did a `cabal freeze` inside
of `nix develop` and then I tried using cabal to build inside and outside of
`nix develop`:
1. `nix develop`
1. `cabal build`
1. `cabal freeze` -- although if i freeze inside nix develop then i can't build
with cabal outside and vice-versa!
1. `ghc --version`: note the version
1. exit `nix develop`
1. Use `ghcup` to set the GHC version to the version shown earlier
1. `cabal build`
Done!
I actually had a bunch of trouble trying to run `cabal build` *outside* of `nix
develop`, because I believe `cabal` kept preferring to use already installed
packages that weren't compatible.
It turns out that for some reason with ghcup I'm restricted to one version of
base, yet I can cabal build with ghc 9.0.2 in develop just fine, so I ended up
restricting the ghc version to 8.10.7 because it's the latest before 4.15 is
hit. All because of the table layout library or whatever. I was able to build
once I set my ghcup GHC version to 8.10.7. So I decided to also use that in my
`flake.nix`:
```
basePackages = pkgs.haskell.packages.ghc8107;
```
Actually I decided NOT to use the above because it will take SUCH A LONG
TIME/not use the cache.
I'm not sure why I could see the GHC version in my `nix develop` was `9.0.2` and
could access the earlier `base` dependency, yet outside of `nix develop` it
could not.
I set this in my `burrow.cabal`:
```
tested-with: GHC==8.10.7
```
I also created a `cabal.project` (weirdly the order of the fields mattered, I
think, for `nix develop`):
```
packages:
./.
with-compiler: ghc-8.10.7
```
Although the `with-compiler` will break buildling in nix develop? I had lots of
weird issues with `nix-develop`, like it takes so incredibly long to launch (not
caching, yet `nix build` does?).
Then after all that I advice checking doing `cabal freeze` in `nix develop` and
rebuilding.
after a lot of troubleshooting trying to get cabal freeze to work in both nix
develop and regular cabal i noticed the cabal produced by regular had later zlib
version than nix, so I tried setting the nix version in the nix file
don't forget to use your `ghcup` install carefully (using right ghc).
In the end I decided not to worry about `cabal freeze`.
## VS Code
direnv (carefully follow instructions) and add this to project root (`hie.yaml`
I guess so HLS knows to use cabal not stack or whatever):
```
cradle:
cabal:
```
you can also specify `stack` instead if you want.
i think this file above helps HLS detect that you're only using cabal and not
using Stack (you may get errors about cradle otherwise). generally helpful maybe
install these plugins to vscode:
* direnv by cab404
* haskell by haskell
* i think nix env selector doesn't work with flakes?
* nix ide
This link (reddit) was helpful:
https://www.reddit.com/r/NixOS/comments/v23c3k/how_are_nix_flakes_used_in_vscode/
```
echo use flake > .envrc
direnv allow
```
also
```
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "nixEnvSelector.nixFile": "${workspaceFolder}/flake.nix"
+}
```
## Crazy caveat
I ran into this weird issue where when I try to `nix build` it'd complain about
a file not existing,e ven though I could `cabal build` and it'd detect the
source file just fine. It turns out I had to `git add` the file for nix to pick
up on the file?!