20 views
# DeCal lecture on Nix ![](https://nixos.org/_astro/nixos-logo-default-gradient-black-regular-horizontal-none.DJqPU_wa_x6tmb.svg) ## 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