"The key to understanding Nix code is knowing where each variable comes from."
Defining Variables: let ... in#
This is the most common way to define local variables.
let
myUser = "alice";
myShell = pkgs.zsh;
in {
users.users.${myUser} = {
shell = myShell;
};
}- Rule:
letbindings are visible within every binding's value and the expression afterin, so bindings can refer to one another. They do not leak outside the expression.
Shorthand Magic: inherit#
Use inherit when you want to assign a variable to an attribute with the same name.
Conventional form:
{
git = pkgs.git;
vim = pkgs.vim;
}Shorthand with inherit:
{
inherit (pkgs) git vim;
}This is extremely common in flake.nix and module configurations.
Caution: with pkgs;#
with set; body adds an attribute-lookup scope for body. Existing lexical bindings take precedence; it does not turn the attributes into ordinary let bindings.
with pkgs; [ git vim htop ]- Risk: variable-name collisions become easy to introduce and difficult to debug, since it may be unclear whether a variable comes from
pkgsor the global scope. - Recommendation: using it inside lists
[ ... ]is fine, but use it sparingly in large configuration blocks.
Functions and Parameter Breakdown#
The top level of a Nix file is often an anonymous function:
{ config, pkgs, lib, ... }: {
# 配置内容
}...(Ellipsis): indicates that the function accepts additional arguments it does not currently need, preventing errors from an argument mismatch.- Destructuring: specify the parameter names you need directly inside the braces.
Practical Tip: Nested Scopes#
let
commonPkgs = with pkgs; [ git vim ];
in {
environment.systemPackages = commonPkgs ++ (let extra = [ pkgs.htop ]; in extra);
}The ++ operator combines two lists.#
References & further reading
Adapted from technical notes. Examples use generic names.