You can already read values, sets, and functions. That is most of Nix.
Open a real flake.nix anyway and it can still look alien, because working Nix
leans on three shortcuts you have not met. All three exist for the same reason:
Nix hates repetition. None of them changes what an attribute set
actually is.
Writing name = name; feels silly, and Nix agrees with you. inherit grabs a
name from the surrounding scope and drops it into the set under that same name.
Hover inherit and you’ll see the same set written out the long way.
One inherit will take as many names as you throw at it, the way inherit a b c;
does.
Bring the shell across as well.
Put a set in brackets and the names come out of that set instead of the surrounding scope.
Read inherit (user) name; as “take name, from user”. It is precisely the
same as writing name = user.name;, which is exactly what the hover will tell
you.
This form takes several names too: inherit (user) a b;.
Take the shell from the user too, and leave the port behind.
Get comfortable with this one. It turns up constantly in package files, where a handful of names get picked out of a much larger set.
Imagine you want to reuse a set value inside itself. A normal set cannot use its own
names, because those names only exist for whoever ends up holding the finished
set. Which is why this needs a let sitting above it:
rec cuts out the middleman. The set becomes recursive, and any value inside
it may use any name inside it.
Order still doesn’t matter, because Nix is lazy.
Ship version 2.0, and let the name follow on its own.
You changed one value and two of them moved. That is the appeal, and it is also
exactly why a large rec set gets so hard to follow.
// hands back the left set with the right set laid over the top of it. Wherever
both name the same attribute, the right side wins.
This is how “sensible defaults, plus my changes” gets spelled in Nix.
Change the host to example.com.
That went through, because nothing on the right had an opinion about host.
Now try changing port in defaults as well. Set it to 9000 and watch the result
flatly refuse to move: the right side already claimed port, so the right side
keeps it.
Editing a default that something further along quietly overrides is a very ordinary afternoon in Nix, and now you know what it feels like from the inside.
There is one more thing about // worth carrying with you. It only merges the
top level, so a nested set on the right replaces the matching one on the left
outright instead of blending into it.
The host is gone. The whole service set got replaced, not updated.
If you are now wondering how a NixOS configuration combines nested settings from a
dozen files without losing things like that, the answer is that it doesn’t use
// at all. The module system merges one option at a time, and the option’s own
type is what decides how. The modules lesson is where that gets taken apart.
inherit name;isname = name;, andinherit (set) name;isname = set.name;.rec { }lets a set’s values use the set’s own names.a // blaysbovera, at the top level only.
Each of these carries a real rule about scope, recursion, or replacement. Knowing the longhand sitting behind them beats memorising the punctuation every time.


Share your thoughts