1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
|
---
language: nix
filename: learnnix-de.nix
contributors:
- ["Chris Martin", "http://chris-martin.org/"]
translators:
- ["Dennis Keller", "https://github.com/denniskeller"]
lang: de-de
---
Nix ist eine simple funktionale Programmiersprache, die für den
[Nix package manager](https://nixos.org/nix/) und
[NixOS](https://nixos.org/) entwickelt wurde.
Du kannst Nix Ausdrücke evaluieren mithilfe von
[nix-instantiate](https://nixos.org/nix/manual/#sec-nix-instantiate)
oder [`nix-repl`](https://github.com/edolstra/nix-repl).
```nix
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 bezeichenet 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
* [Nix Manual - Nix expression language]
(https://nixos.org/nix/manual/#ch-expression-language)
* [James Fisher - Nix by example - Part 1: The Nix expression language]
(https://medium.com/@MrJamesFisher/nix-by-example-a0063a1a4c55)
* [Susan Potter - Nix Cookbook - Nix By Example]
(https://ops.functionalalgebra.com/nix-by-example/)
* [Zero to Nix - Nix Tutorial]
(https://zero-to-nix.com/)
* [Rommel Martinez - A Gentle Introduction to the Nix Family]
(https://web.archive.org/web/20210121042658/https://ebzzry.io/en/nix/#nix)
|