Teile diese Seite mit anderen

Lerne X in Y Minuten

Wobei X=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"

]

Weitere Ressourcen


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.