Nix Developer Shells · Workshop Lektion 3 / Sprache

Nix-Sprache II: Sets & Funktionen

Die „Funktionen"-Hälfte von „JSON mit Funktionen". Danach kannst du jede mkShell lesen.

Warum das zählt
mkShell { … } ist ein Funktionsaufruf mit einem Attribute-Set als Argument. Genauso stdenv.mkDerivation { … } und outputs = { … }: …. Wer Sets und Funktionen versteht, versteht den Bauplan von allem in diesem Kurs.

Attribute-Sets

Ein Attribute-Set ist Nix' Kern-Datenstruktur: Name-Wert-Paare in geschweiften Klammern. Jede Zuweisung endet mit einem Semikolon:

{
  name    = "toolbox";
  version = "0.1.0";
  meta    = { license = "MIT"; };   # Sets dürfen verschachteln
}

# Zugriff mit Punkt:
config.meta.license          # => "MIT"

rec – Attribute, die sich selbst kennen

Ein normales Set kann sich nicht selbst referenzieren. rec erlaubt genau das:

rec {
  version = "0.1.0";
  fullName = "toolbox-${version}";   # greift auf version zu – nur dank rec
}

with & inherit: weniger tippen

Zwei Helfer, die du in jeder Dev-Shell sehen wirst:

# with bringt alle Attribute eines Sets in den Scope:
with pkgs; [ git ripgrep jq ]      # statt [ pkgs.git pkgs.ripgrep pkgs.jq ]

# inherit kopiert gleichnamige Namen aus dem Scope ins Set:
let name = "toolbox"; version = "0.1.0"; in
{ inherit name version; }
# entspricht  { name = name; version = version; }

Funktionen

Eine Nix-Funktion nimmt genau ein Argument. Argument und Rumpf trennt ein Doppelpunkt:

double = x: x * 2;          # Aufruf: double 21  => 42

Mehrere Argumente? Verschachtelte Funktionen (das nennt sich Currying):

add = a: b: a + b;           # add 3 4  => 7

Das Set-Pattern – so sehen echte Nix-Funktionen aus

Fast alle wichtigen Funktionen nehmen ein Set als Argument und zerlegen es direkt. Optionale Werte bekommen mit ? einen Default:

mkApp = { name, version ? "0.1.0" }: "${name}-${version}";

mkApp { name = "toolbox"; }              # => "toolbox-0.1.0"
mkApp { name = "toolbox"; version = "2"; }  # => "toolbox-2"

Erkennst du es wieder? pkgs.mkShell { packages = …; shellHook = …; } ist genau dieses Muster: eine Funktion, aufgerufen mit einem Set. ... am Ende des Patterns erlaubt zusätzliche, nicht aufgezählte Attribute:

f = { name, ... }: name;    # ignoriert alle weiteren Attribute

Sets zusammenführen mit //

{ a = 1; b = 2; } // { b = 9; c = 3; }
# => { a = 1; b = 9; c = 3; }   – das rechte Set gewinnt
Dein Win Du kannst jetzt jede Zeile einer flake.nix oder mkShell grammatikalisch einordnen: Das ist ein Set, das ist ein Funktionsaufruf, das ist ein Default-Argument. Ab Lektion 5 geht es nur noch um die Bedeutung – nicht mehr um die Syntax.

Besonderheiten & Stolperfallen

Semikolon vs. nichts In einem Set endet jede Zuweisung mit ;. In einer Liste stehen die Elemente nur durch Leerzeichen getrennt. Verwechslung erzeugt schwer lesbare Fehler.
with sparsam einsetzen with pkgs; ist bequem, aber bei großen Scopes verliert man den Überblick, woher ein Name kommt. In Paket-Definitionen gilt explizites pkgs.foo oft als sauberer.
Eine Funktion = ein Argument Es gibt keine Mehr-Argument-Funktion in Nix. „Mehrere Argumente" sind entweder verschachtelte Funktionen oder – der Normalfall – ein einziges Set-Argument.

Kurz prüfen (aus dem Kopf)

Nicht spicken – Abrufen aus dem Gedächtnis ist genau die Übung, die hängen bleibt.

Was ist pkgs.mkShell { packages = […]; } grammatikalisch?

Wozu dient rec vor einem Attribute-Set?

Was ergibt { a = 1; } // { a = 9; b = 2; }?

Übung für Teilnehmende

Ziel: Eine eigene kleine Funktion mit Set-Pattern schreiben und aufrufen.

  1. Schreibe im nix repl eine Funktion mkTool, die ein Set { name, version ? "0.1" } nimmt und "name-version" zurückgibt.
  2. Rufe sie einmal mit und einmal ohne version auf.
  3. Führe zwei Sets mit // zusammen und beobachte, welcher Wert gewinnt.

Tipp: Funktionen kannst du im repl direkt einer Variablen zuweisen oder inline aufrufen.

Lösung anzeigen
nix-repl> mkTool = { name, version ? "0.1" }: "${name}-${version}"
nix-repl> mkTool { name = "toolbox"; }
"toolbox-0.1"
nix-repl> mkTool { name = "toolbox"; version = "2.0"; }
"toolbox-2.0"
nix-repl> { a = 1; } // { a = 9; b = 2; }
{ a = 9; b = 2; }

Du hast gerade das Muster gebaut, nach dem mkShell und mkDerivation funktionieren.

Primärquelle zum Lesen nix.dev – Nix language basics. Lies diesmal „Attribute set", „Recursive sets", „with", „inherit" und „Functions". Das ist die komplette Grammatik, die wir ab jetzt nur noch anwenden.
Ich bin dein Teacher. Currying verwirrend? Unsicher, wann with sinnvoll ist? Schreib mir ein Beispiel in den Chat, wir werten es zusammen aus.
← Lektion 2 · Werte & Strukturen Lektion 4 · Nixpkgs & pkgs →
Als Nächstes: Lektion 4 – Nixpkgs & pkgs (das Paket-Set, Pakete finden)