- “Nix” the programming language…
- …executed by the “Nix Daemon”…
- …which interacts with the “Nix” CLIs
- Language: Nix
- Daemon:
nix-daemon
- CLIs:
nix-<command>
andnix <command>
- Stdlib:
nixpkgs
- Attribute set (or “attrset”)
- A map of keys and values of
arbitrary types. Key = Value pairs must end with a
;
.{ a = 13; b = 12; c = 11; hello = true; }
- Lists
- A list of arbitrary types. Can contain mixed types.
Values are space-separated.
[ 13 12 11 "hello" true ]
- Functions
- A function to call. Can both be named
(
fetchFromGitHub
), or anonymous.map (x: x * 2) [ 2 4 6 8 10 ]
- Strings
- UTF-8 literals.
Two variants: inline (via
"..."
) and block (via''...''
). Block strings expand\n
. Both strings support iterpolation with${}
(called “dollar-curly”).
- Integers (
5
,14
,42
) - Doubles (
4.15151515
) - Booleans (
true
andfalse
) - Paths (
/nix/store
or./my-module
) - Null (
null
)
The semantics of Nix is assignments all the way down.
{
package = pkgs.wine.override {
wineBuild = "wine64";
wineRelease = "staging";
};
}
{
package = pkgs.wine.override {
wineBuild = "wine64";
wineRelease = "staging";
};
}
package =
defines a key with somethingpkgs.wine.override { ... }
is a functionwineBuild
andwineRelease
are two keys in an attribute set, passed towine.override
let wine = pkgs.wine.override {
wineBuild = "wine64";
wineRelease = "staging";
};
in {
package = wine;
}
- Pre-define a set of variables for a given scope
- There are no global variables, only scope-specific bindings
let package = pkgs.wine.override {
wineBuild = "wine64";
wineRelease = "staging";
};
in {
inherit package;
}
- Take a value from one scope and copy it to another
- Essentially
{ inherit foo; }
is the same as writing{ foo = foo; }
.
- Not technically a keyword but a
builtin
- Loads and parses the nix expression at the given path
# config.nix
{ logging = "debug"; port = 8080; open = true; }
# default.nix
{ name = "my-application-service"; config = import ./config.nix; }
- Loads a scope into the following nix expression
- Makes all keys available
- Considered a little bit controvertial
with lib;
{
src = with pkgs; fetchFromGitHub {
owner = "spacekookie";
repo = "ddos";
sha256 = fakeSha256;
};
}
nix-repl> { productRelease = currentYear - 8; currentYear = 2022; }.productRelease
error: undefined variable 'currentYear'
at «string»:1:20:
1| { productRelease = currentYear - 8; currentYear = 2022; }.productRelease
| ^
- By default attribute sets are not recursively self-referencial (for performance reasons)
- To enable this, mark an attribute set with the
rec
keyword - Alternatively you can usually always use a
let ... in
block
nix-repl> rec { productRelease = currentYear - 8; currentYear = 2022; }.productRelease
2014
Let’s look at a function which accepts an attribute set.
let
xySum = attrs: attrs.x + attrs.y;
in
xySum { x = 5; y = 7; }
- What happens when we call this function without an attribute set? Or with missing values?
What happens when we…
- pass the wrong type (
xySum 5
) error: value is an integer while a set was expected
- pass an incomplete set (
xySum { x = 5; }
) error: attribute 'y' missing
Furthermore, writing attrs.<value>
repeatedly will get annoying
quickly.
Declare the function as accepting an attribute set, with a specific set of keys.
let
xySum = { x, y }: x + y;
in
xySum { x = 5; y = 7; }
There are some other options you have for making functions with named parameters (i.e. which accept an attribute set) easier to use.
- Allow additional parameters
{ x, y, ... }: x + y
- Assume defaults
{ x, y ? 7 }: x + y
- Bind the full set
{ x, y ? 7 } @ set: otherFunction (x + y) set
let
function = { a ? 23, ... } @ args: args;
in
function { }
What does this function return?
Isn’t that delightfully confusing?
let
function = { a ? 23, ... } @ args: { inherit a; } // args;
in
function { }
- Nix uses a lookup path for loading modules called
NIX_PATH
- Arbitrary keys and values can exist
- Values can be retrieved via the “diamond reference”
nix-repl> <nixpkgs>
/home/sys
nix-repl> <modules>
/home/sys/modules
Sometimes you want to merge two attribute sets, or append one list onto another!
{ a = 13; b = 12; } // { c = 11; }
- results in
{ a = 13; b = 12; c = 11; }
[ 13 12 ] ++ [ 11 ]
- results in
[ 13 12 11 ]
Let’s take the example builder from earlier. Can we understand what happens here?
with import <nixpkgs> {};
let
myPython = pkgs.python3.withPackages (pypkgs:
with pypkgs; [ request flask prometheus_client pendulum ]);
in
stdenv.mkDerivation {
name = "prometheus-weather-gov";
src = ./.;
buildInputs = with pkgs.python3.pkgs; [
myPython mypy flake8 black
];
}