I am not sure where exactly I am going with this post, but I hope it will be coherent.
Obviously there is a big overlap between the nix and Haskell communities and in fact a lot Haskell projects I came across lately rely on nix on some way or another for building, deploying or developing.
So I have two questions, which bother me, because I feel like those problems don’t have to exist.
1. Build enviroments
It feels like there is no consensus on how to use nix with Haskell. (Which admittedly is a general thing with Haskell and a strength from a different perspective.) And this leads to always having to understand anew how to do things.
My understanding is, when developing software with nix there is one reasonable way to do that.
You have a default.nix
to build your project and a shell.nix
to create your development environment. This has a lot of advantages (for someone familiar with nix), because you can understand this conceptually and can use it for example with direnv/lorri or any other tooling around this setup. Also you can easily use the default.nix
to then install the software to your system.
For a simple Haskell project I had success with something like this. (It’s inspired by some nice blogposts and I think mainly Gabriel439/haskell-nix but I am not sure anymore.)
# default.nix
{ pkgs ? import <unstable> {} }:
let
inherit (pkgs) haskellPackages;
drv = haskellPackages.callCabal2nix "my-project" ./. {};
in
{
my_project = drv;
shell = haskellPackages.shellFor {
withHoogle = true;
packages = p: [drv];
buildInputs = with haskellPackages; [ hlint ghcid cabal-install hindent stack ];
};
}
and
# shell.nix
(import ./. {}).shell
I realize that this might get a little bit more complicated once I need to pin a ghc version. But this looks to me how one would roughly want to do things. It sets up my development environment nicely (the rest are a few lines in my neovim config) and it should work with cabal alone or with cabal and stack.
Now I’d like to develop an app with reflex-frp, but to get a development environment for that it is recommend to either use obelisk where I don’t use nix-shell
myself but a completely opaque to me ob run
. Alternatively I can use reflex-platform where I am supposed to run ./try-reflex
.
Same problem.
I can come up with 2 theories:
- The developers want to make the entry as friction-less as possible and thereby try to abstract everything away, even the fact that
nix
is being used. This might be a benefit for persons complete foreign tonix
, but I am not convinced it does. I feel like this shifts the need to understandnix
a little later in the development process but makes it harder. - Often when things are more complicated than seems necessary to me, it means I haven‘t understood the difficulties of the problem they want to solve. So can it be that it is just not feasible to give an example
default.nix
andshell.nix
? (And I mean it would be totally fine to include some extra derivations, nix-functions or tools which the user needs to install first or via those derivations. Things may be complicated for a project. (In this case ghcjs seems to be a culprit.) Also you can use this with extra binary caches.)
So: Why not just give an example default.nix
and shell.nix
and be done with it? (Or is this done everywhere else in the Haskell world and I picked a bad sample? Not my impression …)
2. Why are so many Haskell packages in nixpkgs broken?
You can see in my example above that I use hindent. I’d love to use brittany instead of hindent, but brittany is marked as broken on 19-03 and unstable. I had similar problems with IHaskell and quite a few other packages. Again I could have picked a bad sample, but still:
That these packages don’t work in nixpkgs baffles me because all this projects are build with nix. Which means there are working nix-expressions. So the transfer into nixpkgs shouldn’t be that hard. Obviously I am wrong about this, please tell me why.
Conclusion
I hope I am not stepping on anyone’s toes here. I realize that a lot of the contributions to all of this happen in the free time of people and I appreciated. I just had some frustrating experiences with Haskell and nix (and I know I am not alone with this). And I’d love to
a) learn why those problems are so frequent and
b) possibly understand how I can circumvent then at least for my purposes. (e.g. how can I get a nix-expression for a reflex project or integrate a working brittany build into my example derivation above.)
Thank you for your attention if you read up to here and I would be very thankful for any insights or corrections.