Language Constructs
Recursive sets
Recursive sets are just normal sets, but the attributes can
refer to each other. For example,
rec {
x = y;
y = 123;
}.x
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,
rec {
x = y;
y = x;
}.x
does not terminateActually, Nix detects infinite
recursion in this case and aborts (infinite recursion
encountered
)..
Let-expressions
A let-expression allows you define local variables for an
expression. For instance,
let
x = "foo";
y = "bar";
in x + y
evaluates to "foobar".
Inheriting attributes
When defining a set 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. For instance,
let x = 123; in
{ inherit x;
y = 456;
}
evaluates to { x = 123; y = 456; }. (Note that
this works because x is added to the lexical scope
by the let construct.) It is also possible to
inherit attributes from another set. For instance, in this fragment
from all-packages.nix,
graphviz = (import ../tools/graphics/graphviz) {
inherit fetchurl stdenv libpng libjpeg expat x11 yacc;
inherit (xlibs) libXaw;
};
xlibs = {
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
xlibs (X11 client-side libraries) set.
Functions
Functions have the following form:
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:
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.,
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
{ 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
(...):
{ 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,
{ 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:
args@{ x, y, z, ... }: z + y + x + args.a
but can also be written as:
{ x, y, z, ... } @ args: z + y + x + args.a
Here args is bound to the entire argument, which
is further matched against the pattern { x, y, z,
... }. @-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.
Note that functions do not have names. If you want to give them
a name, you can bind them to an attribute, e.g.,
let concat = { x, y }: x + y;
in concat { x = "foo"; y = "bar"; }
Conditionals
Conditionals look like this:
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:
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.
Nix expression for Subversion
{ 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;
...
}
show how assertions are
used in the Nix expression for Subversion.
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.
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.
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.)
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,
with e1; e2
introduces the set e1 into the lexical
scope of the expression e2. For instance,
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.,
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.
let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...
establishes the same scope as
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 /*
... */.