x = 1 + 2 does not compute 3 immediately. nix stores a : a pointer to the expression plus the variables in scope. the first time something demands x, nix evaluates the thunk and caches the result. subsequent accesses reuse the cache.
nix does not evaluate 100,000 package definitions. it creates an attribute set of thunks. each package is a suspended computation.
pkgs.curl
only now does nix force the curl thunk. that forces curl's dependencies, which force theirs. the tens of thousands of packages you did not ask for are never touched.
without laziness, import <nixpkgs> {} would take minutes and gigabytes. with it, near-instant.
laziness is not opt-in. every let binding, every attribute, every function body is a thunk until demanded.
nixpkgs is fast to import because nothing evaluates upfront
lib.mkIf in nixos modules works because unevaluated branches are never forced
overrideAttrs is cheap because original attributes are thunks that get replaced, not recomputed
nix-env -qa is slow because listing all packages forces every name and version attribute
derivations is where it meets the store. a derivation is an attribute set that nix knows how to build. everything from chapter 1 and everything from this chapter come together there.