1
0
Fork 0
mirror of https://github.com/NixOS/nix-pills synced 2024-09-19 04:00:13 -04:00
nix-pills/pills/04-basics-of-language.xml
yimmt ca18316768
Change sentence wording in 04-basics-of-language
I think the for is unnecessary and can be confusing (at least for me).
2020-03-21 22:01:48 -04:00

364 lines
13 KiB
XML

<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="basics-of-language">
<title>The Basics of the Language</title>
<para>
Welcome to the fourth Nix pill. In the
<link linkend="enter-environment">previous article</link> we learned about Nix
environments. We installed software as a user, managed their profile, switched
between generations, and queried the Nix store. Those are the very basics of
system administration using Nix.
</para>
<para>
The
<link xlink:href="http://nixos.org/nix/manual/#chap-writing-nix-expressions">Nix language</link>
is used to write expressions that produce derivations. The
<link xlink:href="http://nixos.org/nix/manual/#sec-nix-build">nix-build</link>
tool is used to build a derivations from an expression. Even as a system administrator that
wants to customize the installation, it's necessary to master Nix. Using
Nix for your jobs means you get the features we saw in the previous articles
for free.
</para>
<para>
The syntax of Nix is quite unfamiliar, so looking at existing examples may lead you to
think that there's a lot of magic happening. In reality, it's mostly about
writing utility functions to make things convenient.
</para>
<para>
On the other hand, the same syntax is great for describing packages, so learning the language
itself will pay off when writing package expressions.
</para>
<important><para>
In Nix, everything is an expression, there are no statements. This is common in functional
languages.
</para></important>
<important><para>
Values in Nix are immutable.
</para></important>
<section>
<title>Value types</title>
<para>
Nix 2.0 contains a command named <command>nix repl</command> which is a simple command line tool
for playing with the Nix language. In fact, Nix is a
<link xlink:href="https://nixos.org/nix/manual/#ch-expression-language">pure, lazy, functional language</link>,
not only a set of tools to manage derivations. The <literal>nix repl</literal> syntax is slightly
different to Nix syntax when it comes to assigning variables, but it shouldn't
be confusing so long as you bear it in mind. I prefer to start with <literal>nix repl</literal>
before cluttering your mind with more complex expressions.
</para>
<para>
Launch <literal>nix repl</literal>. First of all, Nix supports basic arithmetic operations:
<literal>+</literal>, <literal>-</literal>, <literal>*</literal> and <literal>/</literal>.
(To exit <literal>nix repl</literal>, use the command <literal>:q</literal>.
Help is available through the <literal>:?</literal> command.)
</para>
<screen><xi:include href="./04/basics.txt" parse="text" /></screen>
<para>
Attempting to perform division in Nix can lead to some surprises.
</para>
<screen><xi:include href="./04/relative-path.txt" parse="text" /></screen>
<para>
What happened? Recall that Nix is not a general purpose language, it's a
domain-specific language for writing packages. Integer division isn't
actually that useful when writing package expressions. Nix parsed
<literal>6/3</literal> as a relative path to the current directory. To get
Nix to perform division instead, leave a space after the
<literal>/</literal>. Alternatively, you can use
<literal>builtins.div</literal>.
</para>
<screen><xi:include href="./04/division.txt" parse="text" /></screen>
<para>
Other operators are <literal>||</literal>, <literal>&amp;&amp;</literal> and <literal>!</literal>
for booleans, and relational
operators such as <literal>!=</literal>, <literal>==</literal>, <literal>&lt;</literal>, <literal>></literal>,
<literal>&lt;=</literal>, <literal>>=</literal>. In Nix, <literal>&lt;</literal>, <literal>></literal>,
<literal>&lt;=</literal> and <literal>>=</literal> are not much used. There are also other operators we will see in the
course of this series.
</para>
<para>
Nix has integer, floating point, string, path, boolean and null
<link xlink:href="http://nixos.org/nix/manual/#ssec-values">simple</link>
types. Then there are also lists, sets and functions. These types are enough
to build an operating system.
</para>
<para>
Nix is strongly typed, but it's not statically typed. That is, you
cannot mix strings and integers, you must first do the conversion.
</para>
<para>
As demonstrated above, expressions will
be parsed as paths as long as there's a slash not followed by a space.
Therefore to specify the current directory, use <literal>./.</literal>
In addition, Nix also parses urls specially.
</para>
<para>
Not all urls or paths can be parsed this way. If a syntax error occurs,
it's still possible to fallback to plain strings. Literal urls and paths
are convenient for additional safety.
</para>
</section>
<section>
<title>Identifier</title>
<para>
There's not much to say here, except that dash (<literal>-</literal>) is allowed in identifiers. That's
convenient since many packages use dash in their names. In fact:
</para>
<screen><xi:include href="./04/dash.txt" parse="text" /></screen>
<para>
As you can see, <literal>a-b</literal> is parsed as identifier, not as
a subtraction.
</para>
</section>
<section>
<title>Strings</title>
<para>
It's important to understand the syntax for strings. When learning to read Nix
expressions, you may find dollars (<literal>$</literal>) ambiguous, but they are very important .
Strings are enclosed by double quotes (<literal>"</literal>), or two single quotes (<literal>''</literal>).
</para>
<screen><xi:include href="./04/strings-basic.txt" parse="text" /></screen>
<para>
In other languages like Python you can also use single quotes for strings (e.g. <literal>'foo'</literal>),
but not in Nix.
</para>
<para>
It's possible to
<link xlink:href="http://nixos.org/nix/manual/#ssec-values">interpolate</link>
whole Nix expressions inside strings with the <literal>${...}</literal> syntax and only that syntax,
not <literal>$foo</literal> or <literal>{$foo}</literal> or anything else.
</para>
<screen><xi:include href="./04/interpolate.txt" parse="text" /></screen>
<para>
Note: ignore the <literal>foo = "strval"</literal> assignment, special syntax in <literal>nix repl</literal>.
</para>
<para>
As said previously, you cannot mix integers and strings. You need to explicitly
include conversions. We'll see this later: function calls are another story.
</para>
<para>
Using the syntax with two single quotes is useful for writing double
quotes inside strings without needing to escape them:
</para>
<screen><xi:include href="./04/double-quotes.txt" parse="text" /></screen>
<para>
Escaping <literal>${...}</literal> within double quoted strings is done with the backslash.
Within two single quotes, it's done with <literal>''</literal>:
</para>
<screen><xi:include href="./04/escaping.txt" parse="text" /></screen>
</section>
<section>
<title>Lists</title>
<para>
Lists are a sequence of expressions delimited by space (<emphasis>not</emphasis> comma):
</para>
<screen><xi:include href="./04/lists.txt" parse="text" /></screen>
<para>
Lists, like everything else in Nix, are immutable. Adding or removing
elements from a list is possible, but will return a new list.
</para>
</section>
<section>
<title>Attribute sets</title>
<para>
Attribute sets are an association between string keys and a Nix values. Keys
can only be strings. When writing attribute sets you can also use unquoted identifiers as
keys.
</para>
<screen><xi:include href="./04/set-basics.txt" parse="text" /></screen>
<para>
For those reading Nix expressions from nixpkgs: do not confuse attribute sets with
argument sets used in functions.
</para>
<para>
To access elements in the attribute set:
</para>
<screen><xi:include href="./04/set-access.txt" parse="text" /></screen>
<para>
Yes, you can use strings to address keys which aren't valid identifiers.
</para>
<para>
Inside an attribute set you cannot normally refer to elements of the same attribute set:
</para>
<screen><xi:include href="./04/set-failed.txt" parse="text" /></screen>
<para>
To do so, use
<link xlink:href="https://nixos.org/nix/manual/#idm140737322044432">recursive attribute sets</link>:
</para>
<screen><xi:include href="./04/set-recursive.txt" parse="text" /></screen>
<para>
This is very convenient when defining packages, which tend to be recursive attribute sets.
</para>
</section>
<section>
<title>If expressions</title>
<para>
These are expressions, not statements.
</para>
<screen><xi:include href="./04/if.txt" parse="text" /></screen>
<para>
You can't have only the <literal>then</literal> branch, you must specify also the <literal>else</literal>
branch, because an expression must have a value in all cases.
</para>
</section>
<section>
<title>Let expressions</title>
<para>
This kind of expression is used to define local variables for inner
expressions.
</para>
<screen><xi:include href="./04/let-basic.txt" parse="text" /></screen>
<para>
The syntax is: first assign variables, then <literal>in</literal>, then an expression which can
use the defined variables. The value of the whole <literal>let</literal> expression will be
the value of the expression after the <literal>in</literal>.
</para>
<screen><xi:include href="./04/let-multiple.txt" parse="text" /></screen>
<para>
Let's write two <literal>let</literal> expressions, one inside the other:
</para>
<screen><xi:include href="./04/let-nested.txt" parse="text" /></screen>
<para>
With <literal>let</literal> you cannot assign twice to the same variable. However, you can
shadow outer variables:
</para>
<screen><xi:include href="./04/let-multiple-assign.txt" parse="text" /></screen>
<para>
You cannot refer to variables in a <literal>let</literal> expression outside of it:
</para>
<screen><xi:include href="./04/let-scope.txt" parse="text" /></screen>
<para>
You can refer to variables in the <literal>let</literal> expression when assigning variables,
like with recursive attribute sets:
</para>
<screen><xi:include href="./04/let-reference.txt" parse="text" /></screen>
<para>
So beware when you want to refer to a variable from the outer scope, but
it's also defined in the current let expression. The same applies to
recursive attribute sets.
</para>
</section>
<section>
<title>With expression</title>
<para>
This kind of expression is something you rarely see in other languages.
You can think of it like a more granular version of <literal>using</literal>
from C++, or <literal>from module import *</literal> from Python. You decide
per-expression when to include symbols into the scope.
</para>
<screen><xi:include href="./04/with-basic.txt" parse="text" /></screen>
<para>
That's it, it takes an attribute set and includes symbols from it in the scope of the inner
expression. Of course, only valid identifiers from the keys of the set will be
included. If a symbol exists in the outer scope and would also be introduced by
the <literal>with</literal>, it will <emphasis>not</emphasis> be shadowed.
You can however still refer to the attribute set:
</para>
<screen><xi:include href="./04/with-scope.txt" parse="text" /></screen>
</section>
<section>
<title>Laziness</title>
<para>
Nix evaluates expression only when needed. This is a great feature when
working with packages.
</para>
<screen><xi:include href="./04/lazy.txt" parse="text" /></screen>
<para>
Since <literal>a</literal> is not needed, there's no error about division by zero, because
the expression is not in need to be evaluated. That's why we can have all
the packages defined on demand, yet have access to specific packages very quickly.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will talk about functions and imports. In this pill I've tried to
avoid function calls as much as possible, otherwise the post would have
been too long.
</para>
</section>
</chapter>