# [[Nix language]] ## Resources ### [Concepts page](https://zero-to-nix.com/concepts/nix-language) by [[Zero to Nix]] - A bit too basic to be practically helpful, however, links to good resources on the bottom ### [Nix language basics](https://nix.dev/tutorials/nix-language) by [[nix.dev]] ### [Reference Manual Page](https://nixos.org/manual/nix/stable/language/) ### List of [Built-in Operators](https://nixos.org/manual/nix/stable/language/operators.html) ### List of [Bulit-in Functions](https://nixos.org/manual/nix/stable/language/builtins.html) - `builtins.fetchGit` - `builtins.fetchTarball` - `builtins.fetchurl` - `builtins.fromJSON` - `builtins.getFlake` ### List of [Nixpkgs library functions](https://nixos.org/manual/nixpkgs/stable/#sec-functions-library) ### [Anti-patterns in the Nix language](https://nix.dev/anti-patterns/language) ### References - [Nix manual: Nix language](https://nixos.org/manual/nix/stable/language/index.html) - [Nix manual: String interpolation](https://nixos.org/manual/nix/stable/language/string-interpolation.html) - [Nix manual: Built-in Functions](https://nixos.org/manual/nix/stable/language/builtins.html) - [Nix manual: `nix repl`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-repl.html) - [Nixpkgs manual: Functions reference](https://nixos.org/manual/nixpkgs/stable/#sec-functions-library) - [Nixpkgs manual: Fetchers](https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers) ### User made docs for `derivation` and `mkDerivation` Because apparently there's [no official documentation](https://github.com/NixOS/nixpkgs/issues/18678) for it - [`derivation`](https://blog.ielliott.io/nix-docs/derivation.html) - [`mkDerivation`](https://blog.ielliott.io/nix-docs/mkDerivation.html) ## [Nix language basics](https://nix.dev/tutorials/nix-language) Tutorial by [[nix.dev]] ```bash nix repl ``` Prepend `:p` to use strict evaluation ```nix nix-repl> { a.b.c = 1; } { a = { ... }; } nix-repl> :p { a.b.c = 1; } { a = { b = { c = 1; }; }; } ``` Use [`nix-instantiate --eval`](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html) to evaluate the expression in a Nix file. ```bash $ echo 1 + 2 > file.nix $ nix-instantiate --eval file.nix 3 ``` - `nix-instantiate --eval` will evaluate default.nix if no file name is specified. Pass `--strict` to `nix-instantiate` to use strict evaluation ```bash $ echo "{ a.b.c = 1; }" > file.nix $ nix-instantiate --eval file.nix { a = <CODE>; } $ echo "{ a.b.c = 1; }" > file.nix $ nix-instantiate --eval --strict file.nix { a = { b = { c = 1; }; }; } ``` ### Whitespace is insignificant The following two are equivalent: ```nix let x = 1; y = 2; in x + y ``` ```nix let x=1;y=2;in x+y ``` ### Attribute sets `{ ... }` Nix vs Json ```nix { string = "hello"; integer = 1; float = 3.141; bool = true; null = null; list = [ 1 "two" false ]; attribute-set = { a = "hello"; b = 2; c = 2.718; d = false; }; # comments are supported } ``` - Attribute names usually do not need quotes. - List elements are separated by white space. ```json { "string": "hello", "integer": 1, "float": 3.141, "bool": true, "null": null, "list": [1, "two", false], "object": { "a": "hello", "b": 1, "c": 2.718, "d": false } } ``` ### Recursive attribute set `rec { ... }` You will sometimes see attribute sets declared with `rec` prepended. This allows access to attributes from within the set. ```nix rec { one = 1; two = one + 1; three = two + 1; } ``` - Elements in an attribute set can be declared in any order, and are ordered on evaluation. This is an [antipattern](https://nix.dev/anti-patterns/language#rec-expression). ### `let ... in ...` - Also known as “`let` expression” or “`let` binding” - `let` expressions allow assigning names to values for repeated use. ```nix let a = 1; in a + a ``` Names can be assigned in any order, and expressions on the right of the assignment (`=`) can refer to other assigned names. ```nix let b = a + 1; a = 1; in a + b ``` Only expressions within the `let` expression itself can access the newly declared names. We say: the bindings have local scope. Counterexample: ```nix { a = let x = 1; in x; b = x; } ``` ``` error: undefined variable 'x' at «string»:3:7: 2| a = let x = 1; in x; 3| b = x; | ^ 4| } ``` ### `let ... in ...` vs `rec { ... }` ```nix let b = a + 1; c = a + b; a = 1; in { c = c; a = a; b = b; } ``` ```nix rec { b = a + 1; c = a + b; a = 1; } ``` The difference is that while a recursive attribute set evaluates to an [attribute set](https://nix.dev/tutorials/nix-language#attrset), any expression can follow after the `in` keyword. ```nix let b = a + 1; c = a + b; a = 1; in [ a b c ] ``` ### Attribute access Attributes in a set are accessed with a dot (`.`) and the attribute name. ```nix let attrset = { x = 1; }; in attrset.x ``` Accessing nested attributes works the same way. ```nix let attrset = { a = { b = { c = 1; }; }; }; in attrset.a.b.c ``` The dot (`.`) notation can also be used for assigning attributes. ```nix { a.b.c = 1; } ``` ### `with ...; ...` The `with` expression allows access to attributes without repeatedly referencing their attribute set. ```nix let a = { x = 1; y = 2; z = 3; }; in with a; [ x y z ] ``` - => `[ 1 2 3 ]` The expression `with a; [ x y z ]` is equivalent to `[ a.x a.y a.z ]`. Attributes made available through `with` are only in scope of the expression following the semicolon (`;`). Counter-example: ```nix let a = { x = 1; y = 2; z = 3; }; in { b = with a; [ x y z ]; c = x; } ``` ``` error: undefined variable 'x' at «string»:10:7: 9| b = with a; [ x y z ]; 10| c = x; | ^ 11| } ``` ### `inherit ...` `inherit` is shorthand for assigning the value of a name from an existing scope to the same name in a nested scope. It is for convenience to avoid repeating the same name multiple times. ```nix let x = 1; y = 2; in { inherit x y; } ``` - => `{ x = 1; y = 2; }` The fragment `inherit x y;` is equivalent to `x = x; y = y;`. It is also possible to `inherit` names from a specific attribute set with parentheses: `inherit (...) ...`. ```nix let a = { x = 1; y = 2; }; in { inherit (a) x y; } ``` The fragment `inherit (a) x y` is equivalent to `x = a.x; y = a.y;`. `inherit` also works inside `let` expressions. ```nix let inherit ({ x = 1; y = 2; }) x y; in [ x y ] ``` => `[ 1 2 ]` ### String Interpolation `${ ... }` - Previously known as “antiquotation”. - The value of a Nix expression can be inserted into a character string with the dollar-sign and braces `${ }`. ```nix let name = "Nix"; in "hello ${name}" ``` Only character strings or values that can be represented as a character string are allowed. Counter-example: ```nix let x = 1; in "${x} + ${x} = ${x + x}" ``` ``` error: cannot coerce an integer to a string at «string»:4:2: 3| in 4| "${x} + ${x} = ${x + x}" | ^ 5| ``` Interpolated expressions can be arbitrarily nested. This can become hard to read, and we recommend to avoid it in practice. ```nix let a = "no"; in "${a + " ${a + " ${a}"}"}" ``` => `"no no no"` Warning: You may encounter strings that use the dollar sign (`
) before an assigned name, but no braces (`{ }`). These are _not_ interpolated strings, but usually denote variables in a shell script. In such cases, the use of names from the surrounding Nix expression is a coincidence. ```nix let out = "Nix"; in "echo ${out} > $out" ``` => `"echo Nix > $out"` Absolute paths ```nix /absolute/path ``` Relative paths - Paths are relative when they contain at least one slash (`/`) but do not start with one. They evaluate to the path relative to the file containing the expression. ```nix ./relative ``` => `/current/directory/relative` You will often see the following expression, which specifies a Nix file’s directory. ```nix ./. ``` => `/current/directory` Two dots (`..`) denote the parent directory. ```nix ../. ``` => `/current` ### Search path Also known as “angle bracket syntax”. ```nix <nixpkgs> ``` => `/nix/var/nix/profiles/per-user/root/channels/nixpkgs` ```nix <nixpkgs/lib> ``` => `/nix/var/nix/profiles/per-user/root/channels/nixpkgs/lib` While you will see many such examples, we recommend to [avoid search paths](https://nix.dev/anti-patterns/language.html#search-path) in practice, as they are [impurities](https://nix.dev/tutorials/nix-language#impurities) which are not reproducible. ### Indented strings (multi-line strings) The Nix language offers convenience syntax for character strings which span multiple lines that have common indentation. ```nix '' multi line string '' ``` => `"multi\nline\nstring\n"` Equal amounts of prepended white space are trimmed from the result. ```nix '' one two three '' ``` => `"one\n two\n three\n"` ### Functions Functions are everywhere in the Nix language and deserve particular attention. A function always takes exactly one argument. Argument and function body are separated by a colon (`:`). Wherever you see a colon (`:`) in Nix language code: - On its left is the function argument - On its right is the function body. Single argument ```nix x: x + 1 ``` Multiple arguments via nesting ```nix x: y: x + y ``` Attribute set argument ```nix { a, b }: a + b ``` Attribute set arg with default attributes ```nix { a, b ? 0 }: a + b ``` Attribute set arg with additional attributes allowed ```nix { a, b, ... }: a + b ``` Named attribute set argument ```nix args@{ a, b, ... }: a + b + args.c ``` or ```nix { a, b, ... }@args: a + b + args.c ``` Functions have no names. We say they are anonymous, and call such a function a _lambda_. ```nix x: x + 1 ``` => `<LAMBDA>` Functions can be assigned to a name. ```nix let f = x: x + 1 in f ``` => `<LAMBDA>` ### Calling functions Also known as “function application”. Calling a function with an argument means writing the argument after the function. ```nix let f = x: x + 1 in f 1 ``` => `2` ```nix let f = x: x.a; in f { a = 1; } ``` => `1` One can also pass arguments by name. ```nix let f = x: x.a; v = { a = 1; }; in f v ``` Since function and argument are separated by white space, sometimes parentheses `()` are needed. ```nix (x: x + 1) 1 ``` => `2` List elements are also separated by white space, therefore the following are different: ```nix let f = x: x + 1; a = 1; in [ (f a) ] ``` => `[ 2 ]` ```nix let f = x: x + 1; a = 1; in [ f a ] ``` => `[ <LAMBDA> 1 ]` #### Multiple arguments Also known as "curried functions". ```nix let f = x: y: x + y; in f 1 2 ``` => `3` The following two are equivalent: ```nix x: y: x + y ``` ```nix x: (y: x + y) ``` #### Attribute set arguments Also known as “keyword arguments” or “destructuring” . Nix functions can be declared to require an attribute set with specific structure as argument. This is denoted by listing the expected attribute names separated by commas (`,`) and enclosed in braces (`{ }`). ```nix {a, b}: a + b ``` The argument defines the exact attributes that have to be in that set. Leaving out or passing additional attributes is an error. ```nix let f = {a, b}: a + b; in f { a = 1; b = 2; } ``` => `3` Calling a function with a default argument ```nix let f = {a, b ? 0}: a + b in f { a = 1; } ``` => `1` ```nix let f = {a ? 0, b ? 0}: a + b; in f { } # empty attribute set ``` => `0` #### Additional attributes ```nix let f = {a, b, ...}: a + b; in f { a = 1; b = 2; c = 3; } ``` => `3` ### Named attribute set argument Also known as “@ pattern”, “@ syntax”, or “‘at’ syntax”. ```nix let f = {a, b, ...}@args: a + b + args.c; in f { a = 1; b = 2; c = 3; } ``` => `6` ### Function libraries In addition to the [built-in operators](https://nixos.org/manual/nix/stable/language/operators.html) (`+`, `==`, `&&`, etc.), there are two widely used libraries that _together_ can be considered standard for the Nix language. You need to know about both to understand and navigate Nix language code. We recommend to at least skim them to familiarise yourself with what is available. ### `builtins` Also known as “primitive operations” or “primops”. Nix comes with many functions that are built into the language. They are implemented in C++ as part of the Nix language interpreter. - The Nix manual lists all [Built-in Functions](https://nixos.org/manual/nix/stable/language/builtins.html), and shows how to use them. ```nix builtins.toString ``` => `<PRIMOP>` #### `import` Most built-in functions are only accessible through `builtins`. A notable exception is `import`, which is also available at the top level. `import` takes a path to a Nix file, reads it to evaluate the contained Nix expression, and returns the resulting value. If the path points to a directory, the file `default.nix` in that directory is used instead. ```bash $ echo 1 + 2 > file.nix ``` ```nix import ./file.nix ``` => `3` Since a Nix file can contain any Nix expression, `import`ed functions can be applied to arguments immediately. That is, whenever you see additional tokens after a call to `import`, the value it returns should be a function, and anything that follows are arguments to that function. ### `pkgs.lib` The [`nixpkgs`](https://github.com/NixOS/nixpkgs) repository contains an attribute set called [`lib`](https://github.com/NixOS/nixpkgs/blob/master/lib/default.nix), which provides a large number of useful functions. They are implemented in the Nix language, as opposed to [`builtins`](https://nix.dev/tutorials/nix-language#builtins), which are part of the language itself. ```nix let pkgs = import <nixpkgs> {}; in pkgs.lib.strings.toUpper "search paths considered harmful" ``` => `SEARCH PATHS CONSIDERED HARMFUL` Fully reproducible example ```nix let nixpkgs = fetchTarball https://github.com/NixOS/nixpkgs/archive/06278c77b5d162e62df170fec307e83f1812d94b.tar.gz; pkgs = import nixpkgs {}; in pkgs.lib.strings.toUpper "always pin your sources" ``` => `ALWAYS PIN YOUR SOURCES` What you will also often see is that `pkgs` is passed as an argument to a function. By convention one can assume that it refers to the Nixpkgs attribute set, which has a `lib` attribute. Oftentimes you will see in NixOS configurations, and also within Nixpkgs, that `lib` is passed directly. In that case one can assume that this `lib` is equivalent to `pkgs.lib` where only `pkgs` is available. ```nix { lib, ... }: let to-be = true; in lib.trivial.or to-be (! to-be) ``` => `<LAMBDA>` Sometimes both `pkgs` and `lib` are passed as arguments. In that case, one can assume `pkgs.lib` and `lib` to be equivalent. This is done to improve readability by avoiding repeated use of `pkgs.lib`. ```nix { pkgs, lib, ... } ... multiple uses of `pkgs` ... multiple uses of `lib` ``` ## Impurities ### Paths Whenever a file system path is used in [string interpolation](https://nix.dev/tutorials/nix-language#string-interpolation), the contents of that file are copied to a special location in the file system, the _Nix store_, as a side effect. The evaluated string then contains the Nix store path assigned to that file. Example: ```bash echo 123 > data ``` ```nix "${./data}" ``` => `"/nix/store/h1qj5h5n05b5dl5q4nldrqq8mdg7dhqk-data"` ### Fetchers Files to be used as build inputs do not have to come from the file system. The Nix language provides built-in impure functions to fetch files over the network during evaluation: - [builtins.fetchurl](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchurl) - [builtins.fetchTarball](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchTarball) - [builtins.fetchGit](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit) - [builtins.fetchClosure](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchClosure) These functions evaluate to a file system path in the Nix store. ```nix builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz" ``` => `"/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"` Some of them add extra convenience, such as automatically unpacking archives. ```nix builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz" ``` => `"/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"` ## Derivations A build task in Nix is called a _derivation_. Build tasks are at the core of both Nix and the Nix language: - The Nix language is used to describe build tasks. - Nix runs build tasks to produce _build results_. - Build results can in turn be used as inputs for other build tasks. The Nix language primitive to declare a build task is the built-in impure function `derivation`. It is usually wrapped by the Nixpkgs build mechanism `stdenv.mkDerivation`, which hides much of the complexity involved in non-trivial build procedures. **Note**: You will probably never encounter `derivation` in practice. Whenever you see `mkDerivation`, it denotes something that Nix will eventually _build_. The evaluation result of `derivation` (and `mkDerivation`) is an [attribute set](https://nix.dev/tutorials/nix-language#attrset) with a certain structure and a special property: It can be used in [string interpolation](https://nix.dev/tutorials/nix-language#string-interpolation), and in that case evaluates to the Nix store path of its build result. ```nix let pkgs = import <nixpkgs> {}; in "${pkgs.nix}" ``` => `"/nix/store/sv2srrjddrp2isghmrla8s6lazbzmikd-nix-2.11.0"` - The example imports the Nix expression from the search path `<nixpkgs>`, and applies the resulting function to an empty attribute set `{}`. Its output is assigned the name `pkgs`. - Converting the attribute `pkgs.nix` to a string with [string interpolation](https://nix.dev/tutorials/nix-language#string-interpolation) is allowed, as `pkgs.nix` is a derivation. That is, ultimately `pkgs.nix` boils down to a call to `derivation`. - The resulting string is the file system path where the build result of that derivation will end up. - There is more depth to the inner workings of derivations, but at this point it should be enough to know that such expressions evaluate to Nix store paths. String interpolation on derivations is used to refer to other build results as file system paths when declaring new build tasks. This allows constructing arbitrarily complex compositions of derivations with the Nix language. ## Examples ### Shell environment ```nix { pkgs ? import <nixpkgs> {} }: let message = "hello world"; in pkgs.mkShell { buildInputs = with pkgs; [ cowsay ]; shellHook = '' cowsay ${message} ''; } ``` This example declares a shell environment (which runs the `shellHook` on initialization). Explanation: - This expression is a function that takes an attribute set as an argument. - If the argument has the attribute `pkgs`, it will be used in the function body. Otherwise, by default, import the Nix expression in the file found on the search path `<nixpkgs>` (which is a function in this case), call the function with an empty attribute set, and use the resulting value. - The name `message` is bound to the string value `"hello world"`. - The attribute `mkShell` of the `pkgs` set is a function that is passed an attribute set as argument. Its return value is also the result of the outer function. - The attribute set passed to `mkShell` has the attributes `buildInputs` (set to a list with one element: the `cowsay` attribute from `pkgs`) and `shellHook` (set to an indented string). - The indented string contains an interpolated expression, which will expand the value of `message` to yield `"hello world"`. ### NixOS configuration ```nix { config, pkgs, ... }: { imports = [ ./hardware-configuration.nix ]; environment.systemPackages = with pkgs; [ git ]; # ... } ``` This example is (part of) a NixOS configuration. Explanation: - This expression is a function that takes an attribute set as an argument. It returns an attribute set. - The argument must at least have the attributes `config` and `pkgs`, and may have more attributes. - The returned attribute set contains the attributes `imports` and `environment`. - `imports` is a list with one element: a path to a file next to this Nix file, called `hardware-configuration.nix`. - `imports` is not the impure built-in `import`, but a regular attribute name! - `environment` is itself an attribute set with one attribute `systemPackages`, which will evaluate to a list with one element: the `git` attribute from the `pkgs` set. - The `config` argument is not (shown to be) used. ### Package ```nix { lib, stdenv }: stdenv.mkDerivation rec { pname = "hello"; version = "2.12"; src = builtins.fetchTarball { url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz"; sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g"; }; meta = with lib; { license = licenses.gpl3Plus; }; } ``` This example is a (simplified) package declaration from Nixpkgs. Explanation: - This expression is a function that takes an attribute set which must have exactly the attributes `lib` and `stdenv`. - It returns the result of evaluating the function `mkDerivation`, which is an attribute of `stdenv`, applied to a recursive set. - The recursive set passed to `mkDerivation` uses its own `pname` and `version` attributes in the argument to the built-in function `fetchTarball`. - The `meta` attribute is itself an attribute set, where the `license` attribute has the value that was assigned to the nested attribute `lib.licenses.gpl3Plus`.