# DeCal lecture on Nix

## Nix language
Nix is
* functional
* lazy
* dynamically typed.
You can follow along by running `nix repl`.
### Attribute sets
```nix
nix-repl> a = { x = 1; y = 2; z = "foobar"; }
Updated a.
nix-repl> a.x + a.y
3
```
Attribute sets can be recursive.
```nix
nix-repl> a = rec { x = 1; y = 2; z = x + y; }
Updated a.
nix-repl> a.z
3
```
But if you are not careful, you will get infinite recursion.
```nix
nix-repl> a = rec { x = y + 1; y = x + 1; }
Updated a.
nix-repl> a.x
error:
… while evaluating the attribute 'x'
at «string»:1:11:
1| a = rec { x = y + 1; y = x + 1; }
| ^
… while evaluating y
at «string»:1:15:
1| a = rec { x = y + 1; y = x + 1; }
| ^
(stack trace truncated; use '--show-trace' to show the full trace)
error: infinite recursion encountered
at «string»:1:26:
1| a = rec { x = y + 1; y = x + 1; }
| ^
```
Why did we only get an error when we evaluated `a.x`? Laziness!
### Functions
Functions always have one argument but can return another function.
Function application does not use the familiar `f()` syntax. Instead, a function is separated from its arguments by whitespace.
```nix
nix-repl> f = a: b: a + b
Updated f.
nix-repl> f "foo" "bar"
"foobar"
nix-repl> g = f "foo"
Updated g.
nix-repl> g "bar"
"foobar"
nix-repl> g "baz"
"foobaz"
```
There is a short-hand syntax for functions whose argument is an attribute set.
```nix
nix-repl> f = { x, y }: x + y
Updated f.
nix-repl> f { x = "foo"; y = "bar"; }
"foobar"
```
### Laziness
```nix
nix-repl> builtins.head (map (x : x * 2) [ 1 "a" ])
2
```
`let` bindings are recursive.
```nix
nix-repl> let x = { a = "foo"; b = "${x.a}bar"; }; in x
{
a = "foo";
b = "foobar";
}
```
### Derivations
```nix
nix-repl> drv = derivation { name = "toy"; system = builtins.currentSystem; builder = "/bin/sh"; args = [ "-c" "echo hello world > $out" ]; }
nix-repl> :b drv
```
mention sandboxing/reproducibility
two different code examples that yield the same drv, don't cause rebuild
## Nixpkgs
https://github.com/NixOS/nixpkgs is a collection of package definitions (for Linux, macOS, and others) written in Nix.
It also provides useful helper functions in `lib/` such as
```nix
$ cd nixpkgs
$ nix repl
Nix 2.34.9
Type :? for help.
nix-repl> :l
Added 28682 variables.
AAAAAASomeThingsFailToEvaluate, AMB-plugins, ArchiSteamFarm, AusweisApp2, CHOWTapeModel, ChowCentaur, ChowKick, ChowPhaser, CoinMP, CuboCore, DisnixWebService, EBTKS, EmptyEpsilon, FIL-plugins, Fabric, LAStools, LASzip, LASzip2, LPCNet, LibreArp
... and 28662 more; view with :ll
nix-repl> lib.foldl (a: b: a + b) "" (lib.filter lib.isString [ "foo" 1 "bar" { } ])
"foobar"
```
### stdenv
<!-- briefly show stdenv? -->
In Nixpkgs, each package is defined by a function whose argument is an attribute set with dependencies and which returns a derivation.
```nix
{
fetchurl,
readline,
stdenv,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "bash";
version = "5.3";
src = fetchurl {
url = "https://ftp.gnu.org/gnu/bash/bash-${finalAttrs.version}.tar.gz";
hash = "sha256-DVzYaWX4aaJs9k9Lcb57lvkKO6iz104n6OnZ1VUPMbo=";
};
buildInputs = [
readline
];
})
```
There are also language-specific helper functions to produce derivations.
* `python3Packages.buildPythonPackage`
* `rustPlatform.buildRustPackage`
* `buildNpmPackage`
* ...
### nix-shell replacing virtualenv
`nix-shell` can spawn a development environment without having to install packages globally, similar to Python's virtualenv.
But we are not restricted to Python packages. 🎉
```
with import <nixpkgs> { };
mkShell {
nativeBuildInputs = [
dmenu
(python3.withPackages (ps: [
ps.numpy
]))
];
}
```
Running `nix-shell --run 'python3 -c "import compression.zstd; print(compression.zstd.zstd_version_info)" | dmenu'` results in `ModuleNotFoundError: No module named 'compression'`.
We can fix this by using `python314.withPackages`.
## NixOS
The same language can be used to configure an entire Linux system in a single file usually called `configuration.nix`.
```nix
{
lib,
modulesPath,
pkgs,
...
}:
{
systemd.services.webserver = {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.netcat-openbsd ];
serviceConfig.ExecStart = pkgs.writeShellScript "webserver.sh" ''
while true; do
echo -e 'HTTP/1.1 200\r\nContent-Type: text/html\r\n\r\n<h1>PONG</h1>' | nc -lvN 80
done
'';
};
networking.firewall.allowedTCPPorts = [
80
];
users.users.root.password = "nixos";
imports = [
(modulesPath + "/virtualisation/qemu-vm.nix")
];
virtualisation.qemu.networkingOptions = lib.mkForce [
"-device e1000,netdev=net0"
"-netdev user,id=net0,hostfwd=tcp::8080-:80"
];
}
```
<!--
```nix
{
lib,
modulesPath,
pkgs,
...
}:
{
systemd.services.webserver = {
wantedBy = [ "multi-user.target" ];
path = [ pkgs.netcat-openbsd ];
script = ''
while true; do
echo -e 'HTTP/1.1 200\r\nContent-Type: text/html\r\n\r\n<h1>PONG</h1>' | nc -lvN 80
done
'';
};
networking.firewall.allowedTCPPorts = [
80
];
users.users.root.password = "nixos";
imports = [
(modulesPath + "/virtualisation/qemu-vm.nix")
];
virtualisation.qemu.networkingOptions = lib.mkForce [
"-device e1000,netdev=net0"
"-netdev user,id=net0,hostfwd=tcp::8080-:80"
];
}
```
-->
<!--
they're familiar with systemd and bash scripting already, propose what it would be like if you put something like those in the nix store?
write a systemd service and use `systemd.services.<name>.script`?
Better `systemd.services.<name>.unitConfig.ExecStart` with `writeShellScript` or `writeShellApplication`.
-->
### nixos-rebuild
Run
```bash
NIXOS_CONFIG=$(pwd)/configuration.nix nixos-rebuild build-vm && ./result/bin/run-nixos-vm
```
and open http://localhost:8080/.
## Modules
There are NixOS modules for lots of programs and services.
Many can be configured fully declaratively.
```nix
{
pkgs,
...
}:
{
services.displayManager.gdm.enable = true;
services.desktopManager.gnome.enable = true;
programs.firefox.enable = true;
environment.systemPackages = [
pkgs.vscodium
];
users.users.nixos = {
isNormalUser = true;
password = "nixos";
};
}
```
### Stable releases
NixOS has two stable releases each year, which are supported for 7 months after being released.
The OCF currently runs NixOS 26.05 and the next release (NixOS 26.11) is coming out at the end of the month.
If you want to help fix broken packages, look at https://github.com/NixOS/nixpkgs/issues/584347 and come to the Nix meetup at the OCF this Friday!
# Links
https://nix.dev/manual/nix/latest/language/
https://nixos.org/manual/nixpkgs/unstable/
https://nixos.org/guides/nix-pills/
https://noogle.dev/
https://search.nixos.org/packages
https://nixos.org/manual/nixos/unstable/
https://search.nixos.org/options
https://wiki.nixos.org/wiki/NixOS_Wiki