the store holds content-addressed paths. references track what depends on what. closures determine what ships. none of that answers where derivations come from.
derivations come from nix expressions.
nix has its own programming language. no IO. no mutation. no print statement. no network access. it evaluates expressions and produces values. those values become derivations.
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation {
pname = "hello";
version = "1.0";
src = ./src;
buildInputs = [ pkgs.gcc ];
}
a function. takes an attribute set. returns an attribute set that describes a derivation. that is the shape of nearly every nix file.
five language features in that expression: attribute sets, functions, string literals, paths, and lists. the language is small. what it builds is not.
primitives covers strings, numbers, paths, booleans, lists, and null.
attribute sets is the data structure everything revolves around. derivations are attribute sets. function arguments are attribute sets. nixpkgs is one big attribute set.
functions covers single-argument lambdas, currying, pattern matching, and builtins.
lazy evaluation is why nixpkgs can define 100,000 packages and nix only evaluates the ones you ask for.