What is the point of using `nix-shell` with a function?

So far, I have seen two “flavors” of default.nix/shell.nix files:

  1. Function

    {pkgs ? import <nixpkgs> {} }:
    
    pkgs.mkShell {
    # declarations
    }
    

    Or, a suggested way when used with niv:

    { sources ? import ./nix/sources.nix
    , pkgs ? import sources.nixpkgs {}
    }:
    # ...
    
  2. Simple expression (not sure what’s the right term here…)

    with (import <nixpkgs> {});
    
    mkShell {
    # declarations
    }
    

    or, to include the explicit-camp:

    let
      pkgs = import <nixpkgs> {};
    in
    pkgs.mkShell {
    # declarations
    }
    

As far as I know, default.nix/shell.nix cannot be given any arguments when used with nix-shell, so declaring them as functions is only a stylistic choice. Indeed, if called with function with head {pkgs}:, the result is

error: cannot auto-call a function that has an argument without a default value ('pkgs')

so it is not like NixOS’ configuration.nix.

Could the function-form be considered an anti-pattern? It threw me off for so long (I may not be alone), and all function heads in default.nix/shell.nix files could be re-written in the non-function form. The latter would also better convey that nix-shell expects a derivation.

For example, the niv example above,

{ sources ? import ./nix/sources.nix
, pkgs ? import sources.nixpkgs {}
}:

would become

let
  sources = import ./nix/sources.nix;
  pkgs = import sources.nixpkgs {};
in

(Although, I have to admit, the former looks more appealing, at least to me.)


The title of the topic is not the most fortunate but all my previous attempts were at least two lines long so any suggestions are welcome. Thank you!

1 Like

You can call these files from other nix code and pass the parameter there. You can also do this with nix-shell, e.g. like this:

nix-shell -E 'import ./test.nix { pkgs = import <unstable> {}; }'
2 Likes

Knew I was missing something basic. Thanks!

Actually you can pass these arguments directly with --arg/--argstr:

$ nix-shell --arg pkgs 'import <nixpkgs> {}'

This isn’t used that often with nix-shell, but nix-build more so. E.g. see hnix’s default.nix which allows building for a different compiler with just

$ nix-build --argstr compiler ghc8101
2 Likes

Now that you mention nix-build, would you share where nix-build and nix-shell is implemented in hnix? I think I found their source in NixOS/nix repo in nix-build.cc (see thread) but it eluded me there.

hnix isn’t a full Nix implementation, I don’t think it supports building derivations, so no nix-build/nix-shell

1 Like