Can I use Haskell's `stack` with `nix run` for a resolver not in my current nixpkgs?

I’m new to Haskell and stack. I’m trying to follow instructions for a university course that assumes Stackage snapshot LTS 14.7
These instructions weren’t made for NixOS, so I’ve translated their

stack --resolver lts-17.4 new helloworld

to

nix run 'nixpkgs#stack' -- --resolver lts-17.4 new helloworld

This gives me:

error: attribute 'ghc8104' missing

       at «string»:1:43:

            1| with (import <nixpkgs> {}); let inputs = [haskell.compiler.ghc8104 git gcc gmp]; libPath = lib.makeLibraryPath inputs; stackExtraArgs = lib.concatMap (pkg: [ ''--extra-lib-dirs=${lib.getLib pkg}/lib''   ''--extra-include-dirs=${lib.getDev pkg}/include'' ]) inputs; in runCommand ''myEnv'' { buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; STACK_PLATFORM_VARIANT=''nix''; STACK_IN_NIX_SHELL=1; LD_LIBRARY_PATH = libPath;STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; LANG="en_US.UTF-8";} ""
             |                                           ^
(use '--show-trace' to show detailed location information)

Fair enough. Stack has Nix integration, but probably can’t magically know which nixpkgs version contains what GHC version. It looks like NixOS 21.05 had GHC 8.10.4 (haskell.compiler.ghc8104), even though it doesn’t show up in search.nixos.org/packages:

So I’ve tried

nix run 'github:NixOS/nixpkgs/nixos-21.05#stack' -- --resolver lts-17.4 new helloworld

but still got

error: attribute 'ghc8104' missing

       at «string»:1:43:

            1| with (import <nixpkgs> {}); let inputs = [haskell.compiler.ghc8104 git gcc gmp]; libPath = lib.makeLibraryPath inputs; stackExtraArgs = lib.concatMap (pkg: [ ''--extra-lib-dirs=${lib.getLib pkg}/lib''   ''--extra-include-dirs=${lib.getDev pkg}/include'' ]) inputs; in runCommand ''myEnv'' { buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; STACK_PLATFORM_VARIANT=''nix''; STACK_IN_NIX_SHELL=1; LD_LIBRARY_PATH = libPath;STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; LANG="en_US.UTF-8";} ""
             |                                           ^
(use '--show-trace' to show detailed location information)

even though

nix run 'github:NixOS/nixpkgs/nixos-21.05#haskell.compiler.ghc8104' -- --version

works just fine and gives me

The Glorious Glasgow Haskell Compilation System, version 8.10.4

Does the flake selection have no effect on Nix evaluations performed by stack thusly invoked?

Do I have to write a shell.nix (or flake.nix) and pin nixpkgs therein? I thought due to stack’s nix integration, I might get around that.

You diagnosed the issue correctly, however, as you can gather from Stack’s Nix Integration’s docs, stack does not inherit the nixpkgs it uses from the nixpkgs it is built from, but just invokes nix-shell which takes nixpkgs from NIX_PATH, so that’s where you need change the used nixpkgs.

As a side note, it is regrettable that stack has no fallback mechanism for the Nix integration and just assumes every GHC is available. OTOH it is debatable whether we should be dropping GHC versions so quickly…

1 Like

To add on to what @sternenseemann said, if you run stack build --verbose, you can see the actual inner nix-shell invocation:

$ stack build --verbose
...
2022-01-26 11:35:08.162494: [debug] Using a nix-shell environment with nix packages: haskell.compiler.ghc8107, git, gcc, gmp
2022-01-26 11:35:08.162610: [debug] Run process: /run/current-system/sw/bin/nix-shell -E "with (import <nixpkgs> {}); let inputs = [haskell.compiler.ghc8107 git gcc gmp]; libPath = lib.makeLibraryPath inputs; stackExtraArgs = lib.concatMap (pkg: [ ''--extra-lib-dirs=${lib.getLib pkg}/lib''   ''--extra-include-dirs=${lib.getDev pkg}/include'' ]) inputs; in runCommand ''myEnv'' { buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; STACK_PLATFORM_VARIANT=''nix''; STACK_IN_NIX_SHELL=1; LD_LIBRARY_PATH = libPath;STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; LANG=\"en_US.UTF-8\";} \"\"" --run "'/nix/store/80mb00i5yqdbbi3iim8mwvfg1qpb3akk-stack-2.7.3/bin/stack' $STACK_IN_NIX_EXTRA_ARGS '--internal-re-exec-version=2.7.3' 'build' '--verbose'" 2.7.3 x86_64 hpack-0.34.5
2022-01-26 11:35:09.460659: [debug] Checking for project config at: /home/illabout/temp/my-proj/stack.yaml
2022-01-26 11:35:09.460801: [debug] Loading project config file stack.yaml
...

If you then look in the nix-shell man page, you can find an explanation of how it determines the nixpkgs to use (as @sternenseemann explained).

In general, if you’re having trouble with stack’s Nix integration, the easiest thing is generally to write a stack-shell.nix. This makes it easy to specify a given Nixpkgs checkout:

I have an example of this in one of my projects:

1 Like

What’s the sanest way to bootstrap this as a novice who doesn’t yet know what they want in their stack.yaml? I’d like my stack.yaml to be created by stack --resolver lts-17.4 new <projectname>, if possible.

My suggestion would be to run the command stack new PROJECT_NAME. It will likely pick lts-18.21 (or whatever the latest resolver is) and create a stack.yaml file using that resolver. Then, you can just manually edit the stack.yaml file to set the resolver to lts-17.4.

1 Like

Thank you! With these hints and some tinkering I was able to come up with the following setup procedure:

  1. Delete “global” (user) project config, as that was LTS 14.7-specific from the previous attempt:

    rm ~/.stack -r
    
  2. Create new stack project with current stackage snapshot:

    nix run 'nixpkgs#stack' -- new myproject
    
  3. Replace resolver in resulting config:

    cd myproject
    sed -i 's@https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/23.yaml@https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/4.yaml@' stack.yaml
    
  4. Amend config with nix settings:

    cat <<EOF >> stack.yaml
    nix:
        packages: []
        pure: true
        shell-file: .nix-helpers/stack-shell.nix
    EOF
    
    Explanation of command-line-fu (click to expand)

    This will append

    nix:
        packages: []
        pure: true
        shell-file: .nix-helpers/stack-shell.nix
    

    to ./stack.yaml

  5. Create nix-shell config for stack:

    mkdir .nix-helpers
    tee .nix-helpers/stack-shell.nix <<EOF
    { pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-21.05.tar.gz") {}
    }:
    
    pkgs.haskell.lib.buildStackProject {
      name = "old";
      ghc = pkgs.haskell.compiler.ghc8104;
    }
    EOF
    
    Explanation of command-line-fu(click to expand)

    This will create .nix-helpers/stack-shell.nix with content

    { pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-21.05.tar.gz") {}
    }:
    
    pkgs.haskell.lib.buildStackProject {
      name = "old";
      ghc = pkgs.haskell.compiler.ghc8104;
    }
    

    to ./stack.yaml

Done. Now I can use e.g.

nix run 'nixpkgs#stack' -- ghci
1 Like

Is the warning

* * * * * * * *

Warning: Multiple files use the same module name:
         * Paths_myproject found at the following paths
           * /home/das-g/tmp/fp/myproject/.stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/autogen/Paths_myproject.hs (myproject:lib)
           * /home/das-g/tmp/fp/myproject/.stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/myproject-exe/autogen/Paths_myproject.hs (myproject:exe:myproject-exe)
* * * * * * * *

something I should worry about? Is it in any way related to the presented setup procedure? (Except for what’s included in the setup procedure above, I didn’t change anything in the newly generated stack project, yet.)

As far as I can tell, everything seems to work fine.

This warning occurs when running ghci

full output (click to expand)

nix run 'nixpkgs#stack' -- ghci

Using main module: 1. Package `myproject' component myproject:exe:myproject-exe with main-is file: /home/das-g/tmp/fp/myproject/app/Main.hs
Building all executables for `myproject' once. After a successful build of all of them, only specified executables will be rebuilt.
myproject> configure (lib + exe)
Configuring myproject-0.1.0.0...
myproject> initial-build-steps (lib + exe)
The following GHC options are incompatible with GHCi and have not been passed to it: -threaded
Configuring GHCi with the following packages: myproject

* * * * * * * *

Warning: Multiple files use the same module name:
         * Paths_myproject found at the following paths
           * /home/das-g/tmp/fp/myproject/.stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/autogen/Paths_myproject.hs (myproject:lib)
           * /home/das-g/tmp/fp/myproject/.stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/myproject-exe/autogen/Paths_myproject.hs (myproject:exe:myproject-exe)
* * * * * * * *

GHCi, version 8.10.4: https://www.haskell.org/ghc/  :? for help
[1 of 3] Compiling Lib              ( /home/das-g/tmp/fp/myproject/src/Lib.hs, interpreted )
[2 of 3] Compiling Main             ( /home/das-g/tmp/fp/myproject/app/Main.hs, interpreted )
[3 of 3] Compiling Paths_myproject  ( /home/das-g/tmp/fp/myproject/.stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/autogen/Paths_myproject.hs, interpreted )
Ok, three modules loaded.
Loaded GHCi configuration from /run/user/1000/haskell-stack-ghci/6543c301/ghci-script
*Main Lib Paths_myproject>

… but not when running stack build or stack run.

full output of build (click to expand)

nix run 'nixpkgs#stack' -- build

Building all executables for `myproject' once. After a successful build of all of them, only specified executables will be rebuilt.
myproject> build (lib + exe)
Preprocessing library for myproject-0.1.0.0..
Building library for myproject-0.1.0.0..
[1 of 2] Compiling Lib
[2 of 2] Compiling Paths_myproject
Preprocessing executable 'myproject-exe' for myproject-0.1.0.0..
Building executable 'myproject-exe' for myproject-0.1.0.0..
[1 of 2] Compiling Main
[2 of 2] Compiling Paths_myproject
Linking .stack-work/dist/x86_64-linux-nix/Cabal-3.2.1.0/build/myproject-exe/myproject-exe ...
myproject> copy/register
Installing library in /home/das-g/tmp/fp/myproject/.stack-work/install/x86_64-linux-nix/a712e25dd66ccd4dd51aa3047df02dbaabeb52242cb3dbb7948ab92386192782/8.10.4/lib/x86_64-linux-ghc-8.10.4/myproject-0.1.0.0-D07AMnmorRCBuuPyMbEg4N
Installing executable myproject-exe in /home/das-g/tmp/fp/myproject/.stack-work/install/x86_64-linux-nix/a712e25dd66ccd4dd51aa3047df02dbaabeb52242cb3dbb7948ab92386192782/8.10.4/bin
Registering library for myproject-0.1.0.0..

full output of nix run 'nixpkgs#stack' -- run:

someFunc

Glad you were able to get this working!

In general, probably not.

If you really want to get this warning to disappear, you may be able to do it by specifying other-modules or auto-modules in the right place in your cabal file. You might get a good answer to this if you asked in a Haskell specific forum (like reddit or stackoverflow):

But I personally wouldn’t worry about it.

1 Like

Sorry for coming late to the party, but I believe my topic is in fact exatly the same (warning: total Haskell noob here, but wanted to get an env with full linting etc. for learning with xmonad config).

I was wondering why we can’t just “fix” the ghc-version issue on the stack side. It should then work with this nice method as well?

So I’m getting (with the above shell and direnv) the following:

> stack init
error: attribute 'ghc8103' missing

       at «string»:1:43:

            1| with (import <nixpkgs> {}); let inputs = [haskell.compiler.ghc8103 git gcc gmp]; libPath = lib.makeLibraryPath inputs; stackExtraArgs = lib.concatMap (pkg: [ ''--extra-lib-dirs=${lib.getLib pkg}/lib''   ''--extra-include-dirs=${lib.getDev pkg}/include'' ]) inputs; in runCommand ''myEnv'' { buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; STACK_PLATFORM_VARIANT=''nix''; STACK_IN_NIX_SHELL=1; LD_LIBRARY_PATH = libPath;STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; LANG="en_US.UTF-8";} ""
             |                                           ^
       Did you mean one of ghc810 or ghc8107?

So the first thing I wondered is: why is with (import <nixpkgs> {}); let inputs = [haskell.compiler.ghc8103 git gcc gmp]; hard-coded?
If we could simply override the compiler ref (to a newer version which is in current nixpkgs) shouldn’t we be all set?

And to achieve that, would one “just” have to change/override the stack expression in nixpkgs?
(I saw it’s huge, haven’t dared to look further into it yet).

EDIT: sorry for the noise, of course it wasn’t “hard-coded” (:man_facepalming: ), though I don’t know where the funky default comes from… The following worked like a charm (while in the above mentioned nix-shell):

> stack --compiler=ghc-9.4 init --force
2 Likes