Nix Haskell Development (2020)

People reading this thread might be interested by two haskell-nix project structures :

2 Likes

Thanks for this write-up! I am trying to adapt some of what you have done here to my Ubuntu system with the Nix package manager installed. I have used NixOS for other efforts but the paths are all different when you are just using the package manager and installing everything with nix-env -i. For example, it’s unclear to me that I have anything like import ./nix/nixos-19-09.nix. The closest I think I have is a near-empty ~/.config/nixpkgs/config.nix and a manifest.nix that is part of my per-user profile. Actually, what I think I want is for the nix-shell to take on the form of just what is returned by callCabal2nix, with maybe some divergences later. Put another way, I’m trying to get the simplest version of your example working with Haskell, on a system with just the Nix package manager. Oh, I also fish shell, which seems to muck everything up for Nix shell. :slight_smile:

I’m glad you liked it!

I actually use fish as well and I use direnv to bring the development environment into fish instead of invoking nix-shell to start development. If you setup direnv with lorri it will load up your last cached version of the development environment (or just an empty environment the first time) so you don’t have to wait for Nix to start hacking on your project.

However, none of this is necessary to get started. Here is a cut down version, two files, you can copy into a simple Haskell project that should just work. Just name it default.nix and put it in the same directory as your cabal file.

{ compiler ? "ghc865", nixpkgs ? import <nixpkgs> {}}:

let

  # This is from the nixos-20.03 branch 
  pinnedPkgs = nixpkgs.fetchFromGitHub {
    owner = "NixOS";
    repo  = "nixpkgs";
    rev = "182f229ba7cc197768488972347e47044cd793fb";
    sha256 = "0x20m7czzyla2rd9vyn837wyc2hjnpad45idq823ixmdhyifx31s";
  };

  pkgs = import pinnedPkgs {};

  gitignore = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ];

  myHaskellPackages = pkgs.haskell.packages.${compiler}.override {
    overrides = hself: hsuper: {
      "test" =
        hself.callCabal2nix
          "test"
          (gitignore ./.)
          {};
    };
  };

  shell = myHaskellPackages.shellFor {
    packages = p: [
      p."test"
    ];
    buildInputs = with pkgs.haskellPackages; [
      myHaskellPackages.cabal-install
      hlint
      brittany
      ghcid
    ];
    withHoogle = true;
  };

  exe = pkgs.haskell.lib.justStaticExecutables (myHaskellPackages."test");

in
  {
    inherit shell;
    inherit exe;
  }

Then rename all instances of test with the name of your project from your cabal file (The place where it says name: project-name-here). And create a simple shell.nix:

(import ./default.nix {}).shell

Now you can run nix-shell to get a shell and nix-build --attr exe to build your executable (It ends up in ./result/bin/project-name). Keep in mind either of these commands might take a while since Nix will pull down ghc (ghc 8.6.5 in this case) and all the libraries you are using.

Note: I’ve included ghcid (the by far the best simple tool to develop Haskell with), hlint for code suggestions and brittany for code formatting.

6 Likes

This post took me on a bit of an adventure. I now have Lorri and direnv and home manager on my Ubuntu system. Never used home manager before, but I’m super happy it exists because I never could get used to nix-env -i for everything, after having used NixOS before. Everything is happy now. Thank you. :slight_smile:

My favorite part is my new “impure” tag on my powerline in fish. lol

1 Like

Hi! I’m looking for a way to integrate a new build step into the standard build phase of my cabal-based cabal derivation (calbal2nix). Unfortunately the current Haskell section in the docs doesn’t provide any clues on how to do that. Any ideas how can I approach this?

random guess: have you tried preBuild/postBuild hooks etc ?

would I just concatenate my postBuild with the original postBuild in overrides? I see there’s one particular postBuild step defined for cabal packages in https://github.com/NixOS/nixpkgs/blob/d2b8b928655f1b5e80985e49555aef70818a9bdf/pkgs/development/haskell-modules/lib.nix#L285-L299 but there’s no shortcuts that would allow it as a convenient method.

Did this ever get published (e.g., on the Nix Wiki), or is this the most up-to-date version of this excellent tutorial?

1 Like

This has not been published to the Nix Wiki and I think I should update it for 2021. Its not that anything above wouldn’t work, but that there are easier and more concise ways of doing Haskell development in Nix in practice. I want to both provide a shorted version for those wanting to get started quickly, but also leave the more verbose version.

In short, one of my new years resolutions is to revisit this and add what I have learned over the past year. If this is not done by the end of the month, feel free to ping me.

11 Likes

The current tooling works well if all of your dependencies can be compiled with the versions of packages specified on stackage. But if some of them don’t (for example if the versions are too new), it can go horribly wrong. You’ll need to manually pick specific versions of packages, and override them in the package set, similar to what you’d usually do in stack.yaml. This process is tedious, error-prone, and needs to be done every time nixpkgs updates. This is also what a dependency solver is for. I think we definitely need something that can take advantage of cabal’s solver to help us generate a build plan. Currently only haskell.nix can do this and I think nixpkgs definitely need this as well.

This issue has been addressed in greater detail in my preview post: A summary of the problems I met while using the current nixpkgs Haskell infrastructure (And my thoughts on how to solve them).

1 Like

Thanks, the trick with overriding mkDerivation helped to pass a custom environment variable into project build with callCabal2nix inside overlay, but this solution causes nix to rebuild all dependencies (hours…).

So an alternative is appreciated.

As for me I think callCabal2nix is very limited in customization.
I don’t understand why passing environment variable is not possible.

.overrideAttrs is not working inside overlay.

I need a way to pass git commit hash. Project is fetched from git as an archive and git meta information is erased, but gitlab CI has CI_COMMIT_SHA env variable, but nix cleans env before launching GHC.

@daniil If you throw together an example of what you’re trying to do in a repo on GitHub (and show exactly what is failing), someone may take a look and try to help you out.

Hi @Skyfold! Wanna write another post for 2022 in 2023?

3 Likes
  • This was written 3 years ago.
  • Wasn’t this a controversial opinion then? What about now? Has anything changed since?
  • As far as I can see, there are still two competing approaches to Nix and Haskell: nixpkgs.haskellPackages vs. haskell.nix. Both exist and haskellPackages isn’t even close to being abandoned or dead.
  • As far as I can see, most tutorials today (including those showing nix flakes with Haskell) are geared towards haskellPackages. (Of course the old Gonzales tutorial, which brought lots of people into the fold, was about haskellPackages but that’s outdated now)
  • This is very confusing to a newcomer in 2023 who has no idea where to begin.
1 Like

Let me try to answer some of your questions. I’ll try to give you objective answers (but my answers will definitely be colored by my own experience).

Sort of. Especially if you consider that this opinion was coming from one of the Haskell maintainers in Nixpkgs.

To give a little historical context, at the time I originally wrote this, the Haskell package set in Nixpkgs wasn’t as big as it is now, and non-main compilers weren’t as well supported. So it made more sense to use haskell.nix if you wanted to use any compiler other than the main GHC in Nixpkgs. In my experience, a lot of people who were using Haskell professionally weren’t using the main GHC in Nixpkgs, but they were either stuck on an older compiler, or using the absolute latest compiler.

Since the above was written there have been a bunch of improvements in Nixpkgs:

  • Platforms other than x86_64-linux are much better supported.
  • There are many more Haskell packages available and compiling correctly.
  • Static-linking with musl works really well now.
  • The non-main GHCs generally have more packages that compile with them. For instance, HLS and its transitive dependency tree will generally compile on all GHCs it supports in Nixpkgs.
  • People have seemed to get pretty comfortable with functions like haskellPackages.callCabal2nix, haskellPackage.callHackage, haskellPackages.developPackage, and haskellPackages.shellFor. You’re starting to really see a lot of use of these, which makes things pretty easy.

In my experience, for a year or two after haskell.nix came out, there were a wave of people that jumped ship from Nixpkgs to haskell.nix. haskell.nix has a bunch of nice things going for it, but it also has a few downsides. In the last two years or so, there have been a non-trivial number of people that have moved back to Nixpkgs.

My guess is that if you look at all serious Haskell projects building with Nix, about half use haskell.nix, and half use Nixpkgs. Or maybe slightly more use haskell.nix.

There are two other recent solutions that may be of interest as well. Both use the Nixpkgs Haskell infrastructure under-the-hood:

Yeah, sorry about that :slight_smile:

My personal recommendation is here:

My recommendations haven’t changed since I wrote this, except I’d say that if you have a Stack-based project and Nixpkgs doesn’t work for you, you might want to check out stacklock2nix before diving into haskell.nix.

3 Likes

And third:

4 Likes

having battled with haskell.nix, and see it turned into an absolute monster, which uses a ton of IFD (bad news for all kinds of reasons).

if you not invested or tied to any particular nix haskell build tool…

then take a look https://horizon-haskell.net/

it’s new, but it’s already proving it’s faster, reliable, stable and flexible…

2 Likes

It’s not immediately clear from their website. Does the horizon thing allow building things like existing stack applications?

DM me, with your details, and I’ll show you a demo.

Do you have a particular stack built app in mind? link me

A post was split to a new topic: Setting up Haskell language server for GHC 9.2.7