aboutsummaryrefslogtreecommitdiff
path: root/doc/manual/src/language/constructs.md
blob: a3590f55df9f5be2732f0f42b6099b3d71ede04c (plain)
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# Language Constructs

## Recursive sets

Recursive sets are like normal [attribute sets](./values.md#attribute-set), but the attributes can refer to each other.

> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}`

Example:

```nix
rec {
  x = y;
  y = 123;
}.x
```

This evaluates to `123`.

Note that without `rec` the binding `x = y;` would
refer to the variable `y` in the surrounding scope, if one exists, and
would be invalid if no such variable exists. That is, in a normal
(non-recursive) set, attributes are not added to the lexical scope; in a
recursive set, they are.

Recursive sets of course introduce the danger of infinite recursion. For
example, the expression

```nix
rec {
  x = y;
  y = x;
}.x
```

will crash with an `infinite recursion encountered` error message.

## Let-expressions

A let-expression allows you to define local variables for an expression.

> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr*

Example:

```nix
let
  x = "foo";
  y = "bar";
in x + y
```

This evaluates to `"foobar"`.

## Inheriting attributes

When defining an [attribute set](./values.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes).
This can be shortened using the `inherit` keyword.

Example:

```nix
let x = 123; in
{
  inherit x;
  y = 456;
}
```

is equivalent to

```nix
let x = 123; in
{
  x = x;
  y = 456;
}
```

and both evaluate to `{ x = 123; y = 456; }`.

> **Note**
>
> This works because `x` is added to the lexical scope by the `let` construct.

It is also possible to inherit attributes from another attribute set.

Example:

In this fragment from `all-packages.nix`,

```nix
graphviz = (import ../tools/graphics/graphviz) {
  inherit fetchurl stdenv libpng libjpeg expat x11 yacc;
  inherit (xorg) libXaw;
};

xorg = {
  libX11 = ...;
  libXaw = ...;
  ...
}

libpng = ...;
libjpg = ...;
...
```

the set used in the function call to the function defined in
`../tools/graphics/graphviz` inherits a number of variables from the
surrounding scope (`fetchurl` ... `yacc`), but also inherits `libXaw`
(the X Athena Widgets) from the `xorg` set.

Summarizing the fragment

```nix
...
inherit x y z;
inherit (src-set) a b c;
...
```

is equivalent to

```nix
...
x = x; y = y; z = z;
a = src-set.a; b = src-set.b; c = src-set.c;
...
```

when used while defining local variables in a let-expression or while
defining a set.

## Functions

Functions have the following form:

```nix
pattern: body
```

The pattern specifies what the argument of the function must look like,
and binds variables in the body to (parts of) the argument. There are
three kinds of patterns:

  - If a pattern is a single identifier, then the function matches any
    argument. Example:
    
    ```nix
    let negate = x: !x;
        concat = x: y: x + y;
    in if negate true then concat "foo" "bar" else ""
    ```
    
    Note that `concat` is a function that takes one argument and returns
    a function that takes another argument. This allows partial
    parameterisation (i.e., only filling some of the arguments of a
    function); e.g.,
    
    ```nix
    map (concat "foo") [ "bar" "bla" "abc" ]
    ```
    
    evaluates to `[ "foobar" "foobla" "fooabc" ]`.

  - A *set pattern* of the form `{ name1, name2, …, nameN }` matches a
    set containing the listed attributes, and binds the values of those
    attributes to variables in the function body. For example, the
    function
    
    ```nix
    { x, y, z }: z + y + x
    ```
    
    can only be called with a set containing exactly the attributes `x`,
    `y` and `z`. No other attributes are allowed. If you want to allow
    additional arguments, you can use an ellipsis (`...`):
    
    ```nix
    { x, y, z, ... }: z + y + x
    ```
    
    This works on any set that contains at least the three named
    attributes.
    
    It is possible to provide *default values* for attributes, in
    which case they are allowed to be missing. A default value is
    specified by writing `name ?  e`, where *e* is an arbitrary
    expression. For example,
    
    ```nix
    { x, y ? "foo", z ? "bar" }: z + y + x
    ```
    
    specifies a function that only requires an attribute named `x`, but
    optionally accepts `y` and `z`.

  - An `@`-pattern provides a means of referring to the whole value
    being matched:
    
    ```nix
    args@{ x, y, z, ... }: z + y + x + args.a
    ```
    
    but can also be written as:
    
    ```nix
    { x, y, z, ... } @ args: z + y + x + args.a
    ```

    Here `args` is bound to the argument *as passed*, which is further
    matched against the pattern `{ x, y, z, ... }`.
    The `@`-pattern makes mainly sense with an ellipsis(`...`) as
    you can access attribute names as `a`, using `args.a`, which was
    given as an additional attribute to the function.

    > **Warning**
    >
    > `args@` binds the name `args` to the attribute set that is passed to the function.
    > In particular, `args` does *not* include any default values specified with `?` in the function's set pattern.
    >
    > For instance
    >
    > ```nix
    > let
    >   f = args@{ a ? 23, ... }: [ a args ];
    > in
    >   f {}
    > ```
    >
    > is equivalent to
    >
    > ```nix
    > let
    >   f = args @ { ... }: [ (args.a or 23) args ];
    > in
    >   f {}
    > ```
    >
    > and both expressions will evaluate to:
    >
    > ```nix
    > [ 23 {} ]
    > ```

Note that functions do not have names. If you want to give them a name,
you can bind them to an attribute, e.g.,

```nix
let concat = { x, y }: x + y;
in concat { x = "foo"; y = "bar"; }
```

## Conditionals

Conditionals look like this:

```nix
if e1 then e2 else e3
```

where *e1* is an expression that should evaluate to a Boolean value
(`true` or `false`).

## Assertions

Assertions are generally used to check that certain requirements on or
between features and dependencies hold. They look like this:

```nix
assert e1; e2
```

where *e1* is an expression that should evaluate to a Boolean value. If
it evaluates to `true`, *e2* is returned; otherwise expression
evaluation is aborted and a backtrace is printed.

Here is a Nix expression for the Subversion package that shows how
assertions can be used:.

```nix
{ localServer ? false
, httpServer ? false
, sslSupport ? false
, pythonBindings ? false
, javaSwigBindings ? false
, javahlBindings ? false
, stdenv, fetchurl
, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
}:

assert localServer -> db4 != null; 
assert httpServer -> httpd != null && httpd.expat == expat; 
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); 
assert pythonBindings -> swig != null && swig.pythonSupport;
assert javaSwigBindings -> swig != null && swig.javaSupport;
assert javahlBindings -> j2sdk != null;

stdenv.mkDerivation {
  name = "subversion-1.1.1";
  ...
  openssl = if sslSupport then openssl else null; 
  ...
}
```

The points of interest are:

1.  This assertion states that if Subversion is to have support for
    local repositories, then Berkeley DB is needed. So if the Subversion
    function is called with the `localServer` argument set to `true` but
    the `db4` argument set to `null`, then the evaluation fails.

    Note that `->` is the [logical
    implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication)
    Boolean operation.

2.  This is a more subtle condition: if Subversion is built with Apache
    (`httpServer`) support, then the Expat library (an XML library) used
    by Subversion should be same as the one used by Apache. This is
    because in this configuration Subversion code ends up being linked
    with Apache code, and if the Expat libraries do not match, a build-
    or runtime link error or incompatibility might occur.

3.  This assertion says that in order for Subversion to have SSL support
    (so that it can access `https` URLs), an OpenSSL library must be
    passed. Additionally, it says that *if* Apache support is enabled,
    then Apache's OpenSSL should match Subversion's. (Note that if
    Apache support is not enabled, we don't care about Apache's
    OpenSSL.)

4.  The conditional here is not really related to assertions, but is
    worth pointing out: it ensures that if SSL support is disabled, then
    the Subversion derivation is not dependent on OpenSSL, even if a
    non-`null` value was passed. This prevents an unnecessary rebuild of
    Subversion if OpenSSL changes.

## With-expressions

A *with-expression*,

```nix
with e1; e2
```

introduces the set *e1* into the lexical scope of the expression *e2*.
For instance,

```nix
let as = { x = "foo"; y = "bar"; };
in with as; x + y
```

evaluates to `"foobar"` since the `with` adds the `x` and `y` attributes
of `as` to the lexical scope in the expression `x + y`. The most common
use of `with` is in conjunction with the `import` function. E.g.,

```nix
with (import ./definitions.nix); ...
```

makes all attributes defined in the file `definitions.nix` available as
if they were defined locally in a `let`-expression.

The bindings introduced by `with` do not shadow bindings introduced by
other means, e.g.

```nix
let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...
```

establishes the same scope as

```nix
let a = 1; in let a = 2; in let a = 3; in let a = 4; in ...
```

## Comments

Comments can be single-line, started with a `#` character, or
inline/multi-line, enclosed within `/* ... */`.