Die „Funktionen"-Hälfte von „JSON mit Funktionen". Danach kannst du jede mkShell lesen.
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.
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 tippenZwei 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; }
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
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
//{ a = 1; b = 2; } // { b = 9; c = 3; }
# => { a = 1; b = 9; c = 3; } – das rechte Set gewinnt
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.
;. 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.
Nicht spicken – Abrufen aus dem Gedächtnis ist genau die Übung, die hängen bleibt.
Was ist pkgs.mkShell { packages = […]; } grammatikalisch?
mkShell ist eine Funktion; das { … } dahinter ist ihr Set-Argument. Genau
dasselbe Muster wie mkDerivation { … }.
Wozu dient rec vor einem Attribute-Set?
In einem rec-Set kann fullName = "x-${version}" auf version im
selben Set zugreifen. Ohne rec wäre version dort nicht sichtbar.
Was ergibt { a = 1; } // { a = 9; b = 2; }?
// führt Sets zusammen; bei Namenskollision gewinnt rechts. Ergebnis:
{ a = 9; b = 2; }. Doppelte Namen sind hier kein Fehler, sondern Absicht (Override).
Ziel: Eine eigene kleine Funktion mit Set-Pattern schreiben und aufrufen.
nix repl eine Funktion mkTool, die ein Set
{ name, version ? "0.1" } nimmt und "name-version" zurückgibt.version auf.// zusammen und beobachte, welcher Wert gewinnt.Tipp: Funktionen kannst du im repl direkt einer Variablen zuweisen oder inline aufrufen.
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.
with sinnvoll ist?
Schreib mir ein Beispiel in den Chat, wir werten es zusammen aus.
pkgs (das Paket-Set, Pakete finden)