Haskell: benchmark dependencies in a shell (shellFor)?

In my flake.nix I define a shell like this

devShells.default = hsPkgs.shellFor {
  packages = _: [ project ];
  doBenchmark = true;
  nativeBuildInputs = with hsPkgs; [
    haskell-language-server
  ];
};

where project is defined using developPackage. AFAIU the doBenchmark = true; should make my benchmark dependencies (the package criterion) available in the devshell, but I don’t see it:

❯ ghc-pkg list | grep crit

❯

My Cabal file contains

benchmark redihs-bench
  import: base
  type: exitcode-stdio-1.0
  hs-source-dirs: bench
  main-is: Bench.hs
  build-depends:
    criterion,
    rehsp,

Have I misunderstood how to use doBenchmark?

From the docs:

This is a shortcut for enabling doBenchmark via genericBuilderArgsModifier

where genericBuilderArgsModifier:

This argument accepts a function allowing you to modify the arguments passed to mkDerivation in order to create the development environment.

Now, this is maybe not super clear, but the way it works is this: shellFor takes hsPkgs and overrides them to use the changed mkDerivation. The resulting overridden package set is then passed to the packages function. Consequently you should never define your package independently of this package set, as you do in your code. You need to take developPackage or any other helpers from the given package set fix point or shellFor well behave unexpectedly or produce broken package environments.

It’s still not super clear. Do you mean that I should add my package to a package set based on hspkgs, e.g. via an override, and then use this new package set’s shellFor and in packages pick my added package?

I’ll try it, but I’m not sure that’s what you mean… maybe there’s an example somewhere of how to use it correctly?

All right, I think I’ve got it.

I add my package to the set with

hsPkgs = haskell.packages.ghc983.override (prevArgs: {
  overrides = newpkgs: oldpkgs: {
    rehsp = hsPkgs.developPackage {
      root = lib.sourceFilesBySuffices ./. [ ".cabal" ".hs" "LICENSE" ];
      name = "rehsp";
      modifier = hl.doBenchmark;
    };
  };
});

and then I create the shell using

devShells.default = hsPkgs.shellFor {
  packages = p: [ p.rehsp ];
  doBenchmark = true;
  nativeBuildInputs = with hsPkgs; [
    cabal-install
    haskell-language-server
  ];
};

I’m I understanding you correctly, @sternenseemann ?

Yes. You technically don’t need to add any packages to the base hsPkgs. You are getting the package set fix point passed to your packages function which allows you to do all sorts of things. In the simplest case you can do something like

haskellPackages.shellFor {
  packages = p: [ (p.developPackage { … }) ];
  # or even packages = p: [ (p.callCabal2nix ...) ];
}

If you need the derivation for something else as well, having it added to a package set you can work with is handy, of course.


Note that you are still being undisciplined about the fix point in your example in the overrides argument you should only be using newpkgs or oldpkgs (if you are changing a package) to refer to packages / functions in the package set.


For the record, you could also just use developPackage with the returnShellEnv argument, but I guess you want additional dev tools?

1 Like