Haskell shell could not find module

I’m trying to create a simple haskell shell with a single package (split) based on haskellPackages.shellFor.

Hence I wrote a shell as so:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs =
    { self, nixpkgs }:
    {
      devShells.x86_64-linux.default =
        let
          p = nixpkgs.legacyPackages.x86_64-linux;
        in
        p.haskellPackages.shellFor {
          packages = h: [
            h.split
          ];

          nativeBuildInputs = [
            p.haskell-language-server
          ];
        };
    };
}

split’s package appears to have the correct structure (?)

$ nix build nixpkgs#haskellPackages.split --no-link --print-out-paths | xargs tree
/nix/store/mg6m6bkkqd98qvnly0kfbnjjr4r6r195-split-0.2.5
└── lib
    └── ghc-9.6.6
        └── lib
            ├── package.conf.d
            │   └── split-0.2.5-ASm9S4CWxpg5KjSJ1ec3hG.conf
<snip...>

I also have a simple test file:

import Data.List.Split

main = undefined

However, trying to build this file results in the following error:

ghc -v test.hs
Glasgow Haskell Compiler, Version 9.6.6, stage 2 booted by GHC version 9.2.4
*** initializing unit database:
package flags []
loading package database /nix/store/2f7lv6caiwwysi4r44s4vy60glj4lbq1-ghc-9.6.6-with-packages/lib/ghc-9.6.6/lib/package.conf.d
wired-in package ghc-prim mapped to ghc-prim-0.10.0
wired-in package ghc-bignum mapped to ghc-bignum-1.3
wired-in package base mapped to base-4.18.2.1
wired-in package rts mapped to rts-1.0.2
wired-in package template-haskell mapped to template-haskell-2.20.0.0
wired-in package ghc mapped to ghc-9.6.6
!!! initializing unit database: finished in 11.62 milliseconds, allocated 3.771 megabytes
*** Chasing dependencies:
Chasing modules from: main:test.hs
!!! Chasing dependencies: finished in 0.35 milliseconds, allocated 0.344 megabytes
Ready for upsweep
  [SingleModule(main:Main []), SingleModule(LN: [main:Main])]
compile: input file test.hs
*** Checking old interface for Main (use -ddump-hi-diffs for more details):
[1 of 2] Compiling Main             ( test.hs, test.o )
*** Parser [Main]:
!!! Parser [Main]: finished in 0.08 milliseconds, allocated 0.075 megabytes
*** Renamer/typechecker [Main]:
!!! Renamer/typechecker [Main]: finished in 2.90 milliseconds, allocated 4.504 megabytes

test.hs:1:1: error:
    Could not find module ‘Data.List.Split’
    Locations searched:
      Data/List/Split.hs
      Data/List/Split.lhs
      Data/List/Split.hsig
      Data/List/Split.lhsig
  |
1 | import Data.List.Split
  | ^^^^^^^^^^^^^^^^^^^^^^

I also checked the contents of the package database dir, and don’t see the split package listed at all:

$ l /nix/store/2f7lv6caiwwysi4r44s4vy60glj4lbq1-ghc-9.6.6-with-packages/lib/ghc-9.6.6/lib/package.conf.d | rg split
lrwxrwxrwx     3 root root  138 Dec 31  1969 splitmix-0.1.0.5-1g4Y67JB8lK4ODDsw0vzN6.conf -> /nix/store/vh2bnim5r7gh3jgcfbm1m15f85gi90p5-splitmix-0.1.0.5/lib/ghc-9.6.6/lib/package.conf.d/splitmix-0.1.0.5-1g4Y67JB8lK4ODDsw0vzN6.conf

How would I go about fixing this shell expression to make the module visible to ghc?

It turns out the correct function to use was the (undocumented) haskellPackages.ghcWithPackages in a regular mkShell:

{
  inputs = {
    nixpkgs.url = "flake:nixpkgs";
  };

  outputs =
    { self, nixpkgs }:
    {
      devShells.x86_64-linux.default =
        let
          p = nixpkgs.legacyPackages.x86_64-linux;
        in
        p.mkShell {
          packages = [
            (p.haskellPackages.ghcWithPackages (h: [
              h.split
            ]))
            p.haskell-language-server
          ];
        };
    };
}

Also, the packages arg to haskellPackages.shellFor is inconsistent with the identically named arg packages to mkShell - rather it’s more akin to inputsFrom in mkShell, i.e. it will put the packages’ dependencies into the shell, not the packages themselves.

1 Like

Yes. You built a shell for split (where split can be compiled), not a shell with split (where split is available and can be used). I think the “For” in the name is the very subtle hint at why.

That’s clear to me after-the-fact, I just think the docs could be massively improved, especially since the correct function isn’t documented in the nixpkgs manual. The docs do not, to me, make it clear enough that shellFor is unsuitable. For example -

packages

This argument is used to select the packages for which to build the development environment.

The only hint here that packages doesn’t work the same as in mkShell is the choice of “for” in that sentence instead of “with” - easily missed when skimming, and one could assume it was a typo anyway.