3

I'm working to understand as much as I can about Nix flakes. I'm puzzled by the fact that a nixpkgs input is usually imported, and the imported value is called as a function. How does the result of import nixpkgs map to code in the nixpkgs flake?

It looks like this use of nixpkgs is common practice in flakes:

# flake.nix
{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    /* ... */
  };

  outputs = { self, flake-utils, nixpkgs /*, ... */ }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = (import nixpkgs) {
          inherit system;
        };
      in
      {
        /* ... */
      }
    );
}

My understanding is that the nixpkgs value in this flake's outputs function is the attribute set produced by the nixpkgs flake. I understand that flake output is a derivation, and a derivation can be imported. But how does the imported value become a function? I expected it to be an attribute set.

I see that the nixpkgs flake includes a lib output. Is there some mechanism where an attribute with a lib attribute path is callable? I have been looking for information on this, but I have not found anything.

If (import nixpkgs) {} is effectively calling that lib attribute, then how does importing differ from calling nixpkgs.lib directly? From what I've read importing a derivation has some effect on either forcing evaluating, or not forcing evaluation of something. I don't understand the details yet.

Jesse Hallett
  • 1,857
  • 17
  • 26

1 Answers1

3

Flakes behave like paths because of their .outPath attribute, which contains the flake source path that is automatically added to all flakes by Nix.

This lets import load the file as a Nix value.

Specifically, because the path is a directory, it loads the default.nix in it, which then loads impure.nix, which contains a function.

Note that the way flakes are evaluated (without --impure), the evaluation is always pure, despite the "impure.nix" file name above.

The .outPath attribute got its name because it is also responsible for making derivations coercible to strings.

Robert Hensing
  • 6,708
  • 18
  • 23
  • Very helpful, thank you! So if I understand correctly, `.outPath` is automatically set on an input as part of the processing that the flake system does when loading inputs, and it is a file system path to directory in the Nix store where the flake is unpacked? I just read that coercing a derivation to a string also evaluates to the derivation's `.outPath` which explains some other things I've seen. And then the `import` doesn't work in flake mode, it uses the earlier system of loading `default.nix`. Ok, makes sense! – Jesse Hallett Nov 16 '22 at 21:59
  • 1
    By "`import` doesn't work in flake mode" I think you mean `import` doesn't load `flake.nix`. I've clarified the answer based on your feedback. – Robert Hensing Nov 16 '22 at 23:50