nixnix-flake

What's the mechanism behind `(import nixpkgs) { ... }` in Nix flakes?


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.


Solution

  • 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.