Lade den Code herunter: learnnix.nix
Nix ist eine simple funktionale Programmiersprache, die für den Nix package manager und NixOS entwickelt wurde.
Du kannst Nix Ausdrücke evaluieren mithilfe von
nix-instantiate
oder nix-repl
.
with builtins; [
# Kommentare
#=========================================
# Inline Kommentare sehen so aus.
/* Multizeilen Kommentare
sehen so aus. */
# Booleans
#=========================================
(true && false) # Und
#=> false
(true || false) # Oder
#=> true
(if 3 < 4 then "a" else "b") # Bedingungen
#=> "a"
# Integers
#=========================================
# Integers sind die einzigen numerischen Typen.
1 0 42 (-3) # Einige integers
(4 + 6 + 12 - 2) # Addition
#=> 20
(7 / 2) # Division
#=> 3
# Strings
#=========================================
"String Literale sind in Anführungszeichen."
"
String Literale können mehrere
Zeilen umspannen.
"
''
Dies wird als Literal mit eingerückten String bezeichnet.
Es entfernt intelligent führende Leerzeichen.
''
''
a
b
''
#=> "a\n b"
("ab" + "cd") # String Konkatenation
#=> "abcd"
# Mit Antiquotation kannst du Werte in Strings einbetten.
("Dein Homeverzeichnis ist ${getEnv "HOME"}")
#=> "Dein Homeverzeichnis ist /home/alice"
# Paths
#=========================================
# Nix besitzt einen primitiven Datentyp für Pfade
/tmp/tutorials/learn.nix
# Ein relativer Pfad wird beim Parsing zu einem absoluten Pfad aufgelöst,
# relativ zu der Datei in der es auftritt.
tutorials/learn.nix
#=> /the-base-path/tutorials/learn.nix
# Ein Pfad muss mindestens einen Schrägstrich enthalten. Ein Pfad für eine
# Datei im selben Verzeichnis benötigt ein ./ Präfix.
./learn.nix
#=> /the-base-path/learn.nix
# Der / Operator muss von Leerraum umgeben sein wenn du dividieren möchtest.
7/2 # Das ist ein Pfadliteral
(7 / 2) # Das ist ein Integerliteral
# Importe
#=========================================
# Eine nix Datei besitzt einen einzelnen top-level Ausdruck mit keinen freien Variablen.
# Ein Import-Ausdruck wird zum Wert der Datei, die importiert wird, ausgewertet.
(import /tmp/foo.nix)
# Importe können ebenso mit Strings spezifiziert werden.
(import "/tmp/foo.nix")
# Import Pfade müssen absolut sein. Pfadliterale
# sind automatisch aufgelöst, das ist ein Ordnung.
(import ./foo.nix)
# Jedoch passiert dies nicht mit Strings.
(import "./foo.nix")
#=> error: string ‘foo.nix’ doesn't represent an absolute path
# Let
#=========================================
# `let` Blöcke erlauben es uns Werte zu Variablen zu binden.
(let x = "a"; in
x + x + x)
#=> "aaa"
# Bindungen können auf sich gegenseitig verweisen. Die Reihenfolge spielt
# keine Rolle.
(let y = x + "b";
x = "a"; in
y + "c")
#=> "abc"
# Innere Bindungen überschatten Äußere.
(let a = 1; in
let a = 2; in
a)
#=> 2
# Funktionen
#=========================================
(n: n + 1) # Funktion, die 1 addiert
((n: n + 1) 5) # Dieselbe Funktion angewendet auf 5.
#=> 6
# Es gibt keine spezielle Syntax für benannte Funktionen, aber sie
# können mit `let` Blöcken, wie jeder andere Wert auch, gebunden werden.
(let succ = (n: n + 1); in succ 5)
#=> 6
# Eine Funktion hat genau ein Argument.
# Mehrere Argumente können erreicht werden mithilfe von Currying.
((x: y: x + "-" + y) "a" "b")
#=> "a-b"
# Benannte Funktionsargumente gibt es auch. Diese werden wir einführen, nachdem wir uns Sets
# angeschaut haben.
# Listen
#=========================================
# Listen werden durch eckige Klammern gekennzeichnet.
(length [1 2 3 "x"])
#=> 4
([1 2 3] ++ [4 5])
#=> [1 2 3 4 5]
(concatLists [[1 2] [3 4] [5]])
#=> [1 2 3 4 5]
(head [1 2 3])
#=> 1
(tail [1 2 3])
#=> [2 3]
(elemAt ["a" "b" "c" "d"] 2)
#=> "c"
(elem 2 [1 2 3])
#=> true
(elem 5 [1 2 3])
#=> false
(filter (n: n < 3) [1 2 3 4])
#=> [ 1 2 ]
# Sets
#=========================================
# Ein "Set" ist eine ungeordnete Zuordnung mit Stringschlüsseln.
{ foo = [1 2]; bar = "x"; }
# Der . Operator nimmt einen Wert aus dem Set.
{ a = 1; b = 2; }.a
#=> 1
# Der ? Operator testet, ob der Schlüssel in dem Set vorhanden ist.
({ a = 1; b = 2; } ? a)
#=> true
({ a = 1; b = 2; } ? c)
#=> false
# Der // Operator mergt zwei Sets.
({ a = 1; } // { b = 2; })
#=> { a = 1; b = 2; }
# Werte auf der rechten Seite überschreiben die Werte auf der linken Seite.
({ a = 1; b = 2; } // { a = 3; c = 4; })
#=> { a = 3; b = 2; c = 4; }
# Das Schlüsselwort rec bezeichnet ein "rekursives Set", in dem sich Attribute
# aufeinander beziehen können.
(let a = 1; in { a = 2; b = a; }.b)
#=> 1
(let a = 1; in rec { a = 2; b = a; }.b)
#=> 2
# Verschachtelte Sets können stückweise definiert werden.
{
a.b = 1;
a.c.d = 2;
a.c.e = 3;
}.a.c
#=> { d = 2; e = 3; }
# Die Nachkommen eines Attributs können in diesem Feld nicht zugeordnet werden, wenn
# das Attribut selbst nicht zugewiesen wurde.
{
a = { b = 1; };
a.c = 2;
}
#=> error: attribute ‘a’ already defined
# With
#=========================================
# Der Körper eines Sets Blocks wird mit der Zuordnung eines Satzes an die Variablen gebunden.
(with { a = 1; b = 2; };
a + b)
# => 3
# Innere Bindungen überschatten äußere Bindungen.
(with { a = 1; b = 2; };
(with { a = 5; };
a + b))
#=> 7
# Die erste Linie diese Tutorials startet mit "with builtins;",
# weil builtins ein Set mit allen eingebauten
# Funktionen (length, head, tail, filter, etc.) umfasst.
# Das erspart uns beispielsweise "builtins.length" zu schreiben,
# anstatt nur "length".
# Set patterns
#=========================================
# Sets sind nützlich, wenn du mehrere Werte einer Funktion
# übergeben musst.
(args: args.x + "-" + args.y) { x = "a"; y = "b"; }
#=> "a-b"
# Dies kann mit Hilfe von Set patterns deutlicher geschrieben werden.
({x, y}: x + "-" + y) { x = "a"; y = "b"; }
#=> "a-b"
# Standardmäßig schlägt das Muster bei Sets mit zusätzlichen Schlüsseln fehl.
({x, y}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
#=> error: anonymous function called with unexpected argument ‘z’
# Durch Hinzufügen von ", ..." können zusätzliche Schlüssel ignoriert werden.
({x, y, ...}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
#=> "a-b"
# Errors
#=========================================
# `throw` bewirkt, dass die Auswertung mit einer Fehlermeldung abgebrochen wird.
(2 + (throw "foo"))
#=> error: foo
# `tryEval` fängt geworfene Fehler.
(tryEval 42)
#=> { success = true; value = 42; }
(tryEval (2 + (throw "foo")))
#=> { success = false; value = false; }
# `abort` ist ähnlich wie throw, aber es ist fatal. Es kann nicht gefangen werden.
(tryEval (abort "foo"))
#=> error: evaluation aborted with the following error message: ‘foo’
# `assert` evaluiert zu dem gegebenen Wert, wenn die Bedingung wahr ist, sonst
# löst es eine abfangbare Exception aus.
(assert 1 < 2; 42)
#=> 42
(assert 1 > 2; 42)
#=> error: assertion failed at (string):1:1
(tryEval (assert 1 > 2; 42))
#=> { success = false; value = false; }
# Impurity
#=========================================
# Da die Wiederholbarkeit von Builds für den Nix Packetmanager entscheidend ist,
# werden in der Nix Sprache reine funktionale Elemente betont. Es gibt aber ein paar
# unreine Elemente.
# Du kannst auf Umgebungsvariablen verweisen.
(getEnv "HOME")
#=> "/home/alice"
# Die trace Funktion wird zum Debugging verwendet. Sie gibt das erste Argument zu stderr aus
# und evaluiert das zweite Argument.
(trace 1 2)
#=> trace: 1
#=> 2
# Du kannst Dateien in den Nix Store schreiben. Obwohl unrein, kannst du dir relativ sicher sein,
# dass es sicher ist, da der Dateiname aus dem Hash des Inhalts abgeleitet wird.
# Du kannst Dateien von überall lesen. In diesem Beispiel schreiben wir Dateien in den Store
# und lesen wieder davon.
(let filename = toFile "foo.txt" "hello!"; in
[filename (builtins.readFile filename)])
#=> [ "/nix/store/ayh05aay2anx135prqp0cy34h891247x-foo.txt" "hello!" ]
# Außerdem können wir Dateien in den Nix Store herunterladen.
(fetchurl "https://example.com/package-1.2.3.tgz")
#=> "/nix/store/2drvlh8r57f19s9il42zg89rdr33m2rm-package-1.2.3.tgz"
]
Du hast einen Verbesserungsvorschlag oder einen Fehler gefunden? Erstelle ein Ticket im offiziellen GitHub Repo, oder du erstellst einfach gleich einen pull request!
Originalversion von Chris Martin, mit Updates von 5 contributors.