Simple nix flake for Haskell development
Recently I've moved over to using flakes in my Haskell development projects. It took me a little while to arrive at a pattern a flake for Haskell development that I like. I'm hoping sharing it might help others when doing the same change
{ inputs = { nixpkgs.url = "github:nixos/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: with nixpkgs.legacyPackages.${system}; let t = lib.trivial; hl = haskell.lib; name = "project-name"; project = devTools: # [1] let addBuildTools = (t.flip hl.addBuildTools) devTools; in haskellPackages.developPackage { root = lib.sourceFilesBySuffices ./. [ ".cabal" ".hs" ]; name = name; returnShellEnv = !(devTools == [ ]); # [2] modifier = (t.flip t.pipe) [ addBuildTools hl.dontHaddock hl.enableStaticLibraries hl.justStaticExecutables hl.disableLibraryProfiling hl.disableExecutableProfiling ]; }; in { packages.pkg = project [ ]; # [3] defaultPackage = self.packages.${system}.pkg; devShell = project (with haskellPackages; [ # [4] cabal-fmt cabal-install haskell-language-server hlint ]); }); }
The main issue I ran into is getting a development shell out of
haskellPackages.developPackage
, it requires returnShellEnv
to be true
.
Something that isn't too easy to find out. This means that the only solution
I've found to getting a development shell is to have separate expressions for
building and getting a shell. In the above flake the build expression, [3],
passes an empty list of development tools, the argument devTools
at [1],
while the development shell expression, [4], passes in a list of tools needed
for development only. The decision of whether the expression is for building or
for a development shell, [2], then looks at the list of development tools
passed in.