Nix 101: Part 1

Jul 29, 2024ยท
Christopher Coverdale
Christopher Coverdale
ยท 3 min read

This post goes over the basics of the Nix Language and in later posts will cover the applications of Nix.

What is Nix?

Nix is a system to create reproducible builds and shell environments. Nix has a purely functional language to express the building of reproducible shell environments.

This post will go over the basics of the language.

Variables

Nix has basic variables found in most high level languages.

string = "Hello, World!";
integer = 1;
bool = true;
list = [1 2 3];

Variables - let..in..

Variables can be defined using the let ... in ... syntax.

The variables are defined in the let scope and then used for evaluation in the in scope.

let
    x = 1;
    y = 2;
in
    x + y

>> 3

Strings

String interpolation is achieved using ${variable}

let
    name = "Foo";
in
    "hello ${name}"

Lists

Lists can be declared using [].

[1 2 3];

Attributes

Attributes are a datastructure, similar to JSON. They are defined using {} and have named key values.

The values can be accessed using dot notation to refer to a key name.

let
    attributes = { x = 1; y = 2; };
in
    attributes.x + attributes.y

>> 3

Recursive Attributes

Recursive Attributes allows the variables within a scope to be referenced using the rec keyword.

rec {
    one = 1;
    two = one + 1;
    three = two + 1;
}

With

Atrributes can be accessed more easily using the with keyword.

with allows access to the attirbutes values without needing to use dot notation.

let
    a = {
        x = 1;
        y = 2;
        z = 3;
    };
in
    with a; [x y z]

Functions

Functions in Nix are lambda functions with a : separating arguments from the function body.

x: x + 1

Attribute sets can be used as arguments.

{a, b}: a + b

Default/optional arguments can be provided using ?.

{a, b ? 0}: a + b

Named arguments can be given using the @ symbol.

args@{a, b, ...}: a + b + args.c

Using let..in.., we can define a function and then call it.

let
    f = x: x + 1;
in
    f 1

We can also pass an attribute set as an argument, which seems to be a common pattern in Nix.

let
    f = x: x.a + 1;
in
    f { a = 1; }

We can also declare functions and variables in the let scope and then apply them.

let
    f = x: x.a + 1;
    v = { a = 1; };
in
    f v

Functions can also be immediately callable by wrapping it around ()

(x: x + 1) 1

Functions can be passed to list and have the function evaluated and the result stored in the list.

let
    f = x: x + 1;
    a = 1;
in [ (f a) ]

Functions can also be passed with out being evaluated along side the arguments.

let
    f = x: x + 1;
    a = 1;
in [ f a ] 

Single attribute sets with multiple values can be used as the argument, as mentioned earlier, this is a common pattern.

let
    f = {a, b}: a + b
in
    f { a = 1; b = 1; };

Inherit

This keyword is shorthand for assigning variables from the previous scope into the new scope.

The below is the equivalent of:

x = a.x;
y = a.y;
let
    a = { x = 1; y = 2; };
in
    {
        inherit (a) x y;
    }

Summary

Nix is a reproducible build system for shells and environments. Nix uses a functional programming language to express and evaluate reproducible builds.

Did you find this page helpful? Consider sharing it ๐Ÿ™Œ