Trouble making direnv and hpack work reliably for Haskell projects

My goal is this to be able to cd into my project, cabal repl, and my hpack powered haskell project be loaded. This has been remarkably difficult.

nix-shell works fine so long as I put a shellHook to call hpack, though nix-shell is a little slow.

I discovered that lorri doesn’t even call shellHooks and spent a lot of time trying to find a workaround without success.

I moved to using nix-community/nix-direnv and I’m encountering weird errors, definitely falling short of the promise of “just cd into this directory and things work” I’ve been trying to achieve.

The good news is nix-shell always works. I can do ghc-pkg list | grep my-project and it’s always there and the cabal file is always generated. With the lorri and nix-direnv based solutions it’s very buggy and it’s hard to tell what exactly is wrong.

In addition to a little venting over the hours I’ve lost, I’m just asking if anyone has cached direnv with Haskell (and hpack) reliably working. I’m betting the “and hpack” part is what’s messing things up for me or there’s a bug with caching for nix-direnv, but overall I feel lost.

Thanks in advance for any help!

1 Like

Alright, this seems to work okay with the default use_nix provided by direnv, though it’s slow because of no caching:

you may want to look at Nix · direnv/direnv Wiki · GitHub , very fast when setup correctly.

if you have a large nix build, you may also want to look at GitHub - target/lorri: Your project's nix-env which will keep onto an evaluation for you.

if you use home-manager, lorri can be enabled with:

  services.lorri.enable = true;

EDIT: looks like you already know about lorri, sorry, just read the first part of your post.

nix-shell works fine so long as I put a shellHook to call hpack, though nix-shell is a little slow.

lorri only work on nix expressions, shell-hooks aren’t captured by it.

I’ll tryout nixify, I think everything worked well in the past when I tried it but somehow forgot about it again :laughing:

Also, I have some discussion on the lorri issue tracker where I’ve gotten good help, but thought it might be easier for myself and the very helpful profspatch if I solicit examples/other user experiences here for common issues.

Nonetheless, that context is useful for our discussions.

I’m working on the lorri version so I can document any issues which I recall still existing even after the latest help in that issue thread.

I’ll also try nixify and see if it “just works” for this case as well.

Maybe I can have each in a different branch and it’ll be useful to others as well.

One problem with nix-direnv is that when you add something to package.yaml it doesn’t invalidate the cache that’s used in your projects .direnv directory so myproj.cabal never gets rebuilt.

Not sure how to get around this one and it requires just calling hpack manually for now.

It looks like nixify puts you at the point of “here is a shell.nix setup with direnv, now go” that looks like:

  # Look here for information about how to generate `nixpkgs-version.json`.
  #  →
  pinnedVersion = builtins.fromJSON (builtins.readFile ./.nixpkgs-version.json);
  pinnedPkgs = import (builtins.fetchGit {
    inherit (pinnedVersion) url rev;

    ref = "{{ref}}";
  }) {};

# This allows overriding pkgs by passing `--arg pkgs ...`
{ pkgs ? pinnedPkgs }:

with pkgs;

mkShell {
  buildInputs = [
    # put packages here.

That’s good generally, but not quite what I was looking for I think. The developPackage and deriving a shell from it method seem better. Are there downsides to it I might not know of?

lorri is most confusing. After changing things to use lorri I did a direnv allow.

I waited for it to finish building, then did cabal repl. cabal tried to start downloading dependencies.

The weirder part is if I do lorri shell and cabal repl, then all my dependencies are present jus tlike nix shell. Then if I exit lorri shell, any subsequent cd’s into my project directory have a state like nix-shell.

I’m guessing this is due to lorri using the cached build that works correctly (but differently) built from lorri shell.

Yep, that’s it. If I rm -r ~/.cache/lorri/ and direnv reload, running cabal repl will not use my nix dependencies. If I do lorri shell again though, close my terminal, open a new terminal, cd to my project, and do cabal repl, all of a sudden my nix dependencies show up.

I think this might be a bug since lorri shell has different semantics and that build with different semantics gets re-used because it’s cached.

Edit: filed a bug on the lorri repo

1 Like

My main goal for this was to have ghcide working transparently in emacs with direnv and direnv-emacs and it seems like the most stable option for that is nix-community/nix-direnv by far.

This isn’t directly related to what you’re asking, but recently Snoyman has been recommending that people check the .cabal files into their repo even if they generate it with hpack:

1 Like

Yeah, I think that’s likely a good idea since then you’ll only get out of sync issues.

I think tools that build haskell projects either need to fully support hpack since users from stack will use it by default or explicitly say hpack/package.yaml is unsupported.

1 Like