1
0
Fork 0
mirror of https://github.com/NixOS/nix-pills synced 2024-09-19 04:00:13 -04:00
nix-pills/pills/05-functions-and-imports.xml
Maximilian Bosch bcea6b35ed
Update documentation after Nix 2.0 switch
In #89 it has been reported that the third step can't be done as
`nix-repl` doesn't evaluate anymore on 18.09 and unstable as in both
cases an evaluation error will be thrown.

First of all this may confuse new users who want to learn Nix, but don't
know the ecosystem sufficiently to understand why the install failed.

As recent NixOS versions (unstable and 18.09) use Nix 2.0 by default and
unstable doesn't evaluate with Nix 1.x anymore it should be a safe thing
to do now.

This patch covers two aspects:

* Using Nix 2.0: I replayed the installation steps with `nix-env` to
  ensure that the steps and explanations provided in the first three
  chapters are still valid.

* Replacing `nix-repl` references: most of the cases it was sufficient
  to replace `nix-repl` with the newly introduced command `nix repl`.
  In chapter three `nix-repl` was used to demonstrate the installation
  of a package with `nix-env`. I decided to use `nix-index` as demo
  package as I figured this tool to be extremely helpful to locate
  packages by output files.

  The explanation that Nix is not only a tool for package/derivation
  management, but a functional language as well was moved to chapter
  four where the basics of the language were actually covered.

This change is just a first step towards an updated series, in the
future we may want to use even more Nix 2.0 features (such as
`nix-build` vs. `nix build` with `nix log`).

Fixes #89
Possibly supersedes #71
2018-12-08 00:10:40 +01:00

340 lines
9.8 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="functions-and-imports">
<title>Functions and Imports</title>
<para>
Welcome to the fifth Nix pill. In the previous <link
linkend="basics-of-language">fourth pill</link> we touched the Nix language
for a moment. We introduced basic types and values of the Nix language, and
basic expressions such as
<code>if</code>, <code>with</code> and
<code>let</code>. I invite you to re-read about these expressions and play
with them in the repl.
</para>
<para>
Functions help to build reusable components in a big repository like
<link xlink:href="https://github.com/NixOS/nixpkgs/">nixpkgs</link>. The Nix
manual has a <link
xlink:href="https://nixos.org/nix/manual/#ss-functions">great explanation of
functions</link>. Let's go: pill on one hand, Nix manual on the other hand.
</para>
<para>
I remind you how to enter the Nix environment: <code>source
~/.nix-profile/etc/profile.d/nix.sh</code>
</para>
<section>
<title>Nameless and single parameter</title>
<para>
Functions are anonymous (lambdas), and only have a single parameter. The
syntax is extremely simple. Type the parameter name, then "<code>:</code>",
then the body of the function.
</para>
<screen><xi:include href="./05/anon-function.txt" parse="text" /></screen>
<para>
So here we defined a function that takes a parameter
<code>x</code>, and returns <code>x*2</code>. The problem is that we cannot
use it in any way, because it's unnamed... joke!
</para>
<para>
We can store functions in variables.
</para>
<screen><xi:include href="./05/named-function.txt" parse="text" /></screen>
<para>
As usual, please ignore the special syntax for assignments inside <literal>nix repl</literal>.
So, we defined a function <code>x: x*2</code> that takes one parameter
<code>x</code>, and returns
<code>x*2</code>. This function is then assigned to the variable
<code>double</code>. Finally we did our first function call: <code>double
3</code>.
</para>
<para>
<emphasis role="underline">Big note:</emphasis> it's not like many other
programming languages where you write
<code>double(3)</code>. It really is <code>double 3</code>.
</para>
<para>
In summary: to call a function, name the variable, then space, then the
argument. Nothing else to say, it's as easy as that.
</para>
</section>
<section>
<title>More than one parameter</title>
<para>
How do we create a function that accepts more than one parameter? For people
not used to functional programming, this may take a while to grasp. Let's do
it step by step.
</para>
<screen><xi:include href="./05/multi-argument-function.txt" parse="text"
/></screen>
<para>
We defined a function that takes the parameter <code>a</code>, the body
returns another function. This other function takes a parameter
<code>b</code> and returns <code>a*b</code>. Therefore, calling <code>mul
3</code> returns this kind of function: <code>b: 3*b</code>. In turn, we
call the returned function with <code>4</code>, and get the expected result.
</para>
<para>
You don't have to use parenthesis at all, Nix has sane priorities when
parsing the code:
</para>
<screen><xi:include href="./05/no-parenthesis.txt" parse="text" /></screen>
<para>
Much more readable, you don't even notice that functions only receive one
argument. Since the argument is separated by a space, to pass more complex
expressions you need parenthesis. In other common languages you would write
<code>mul(6+7, 8+9)</code>.
</para>
<para>
Given that functions have only one parameter, it is straightforward to use
<emphasis role="strong">partial application</emphasis>:
</para>
<screen><xi:include href="./05/partial-application.txt" parse="text"
/></screen>
<para>
We stored the function returned by <code>mul 3</code> into a variable foo,
then reused it.
</para>
</section>
<section>
<title>Arguments set</title>
<para>
Now this is a very cool feature of Nix. It is possible to pattern match over
a set in the parameter. We write an alternative version of <code>mul = a: b:
a*b</code> first by using a set as argument, then using pattern matching.
</para>
<screen><xi:include href="./05/set-argument.txt" parse="text" /></screen>
<para>
In the first case we defined a function that accepts a single parameter. We
then access attributes <code>a</code> and
<code>b</code> from the given set. Note how the parenthesis-less syntax for
function calls is very elegant in this case, instead of doing <code>mul({
a=3; b=4; })</code> in other languages.
</para>
<para>
In the second case we defined an arguments set. It's like defining a set,
except without values. We require that the passed set contains the keys
<code>a</code> and <code>b</code>. Then we can use those <code>a</code> and
<code>b</code> in the function body directly.
</para>
<screen><xi:include href="./05/argument-set-error.txt" parse="text"
/></screen>
<para>
Only a set with exactly the attributes required by the function is accepted,
nothing more, nothing less.
</para>
</section>
<section>
<title>Default and variadic attributes</title>
<para>
It is possible to specify <emphasis role="strong">default values</emphasis>
of attributes in the arguments set:
</para>
<screen><xi:include href="./05/default-values.txt" parse="text" /></screen>
<para>
Also you can allow passing more attributes (<emphasis
role="strong">variadic</emphasis>) than the expected ones:
</para>
<screen><xi:include href="./05/variadic-arguments.txt" parse="text"
/></screen>
<para>
However, in the function body you cannot access the "c" attribute. The
solution is to give a name to the given set with the <emphasis
role="strong">@-pattern</emphasis>:
</para>
<screen><xi:include href="./05/named-set-argument.txt" parse="text"
/></screen>
<para>
That's it, you give a name to the whole parameter with name@ before the set
pattern.
</para>
<para>
Advantages of using argument sets:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
Named unordered arguments: you don't have to remember the order of the
arguments.
</para>
</listitem>
<listitem>
<para>
You can pass sets, that adds a whole new layer of flexibility and
convenience.
</para>
</listitem>
</itemizedlist>
<para>
Disadvantages:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
Partial application does not work with argument sets. You have to
specify the whole attribute set, not part of it.
</para>
</listitem>
</itemizedlist>
<para>
You may find similarities with <link
xlink:href="https://docs.python.org/2/faq/programming.html#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another">Python
**kwargs</link>.
</para>
</section>
<section>
<title>Imports</title>
<para>
The <code>import</code> function is built-in and provides a way to parse a
<filename>.nix</filename> file. The natural approach is to define each
component in a <filename>.nix</filename> file, then compose by importing
these files.
</para>
<para>
Let's start with the bare metal.
</para>
<para>
<filename>a.nix</filename>:
</para>
<programlisting><xi:include href="./05/a-nix.txt" parse="text"
/></programlisting>
<para>
<filename>b.nix</filename>:
</para>
<programlisting><xi:include href="./05/b-nix.txt" parse="text"
/></programlisting>
<para>
<filename>mul.nix</filename>:
</para>
<programlisting><xi:include href="./05/mul-nix.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/import.txt" parse="text" /></screen>
<para>
Yes it's really that straight. You import a file, and it gets parsed as
expression. Note that the scope of the imported file does not inherit the
scope of the importer.
</para>
<para>
<filename>test.nix</filename>:
</para>
<programlisting><xi:include href="./05/test-nix.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/test-import.txt" parse="text" /></screen>
<para>
So how do we pass information to the module? Use functions, like we did with
<filename>mul.nix</filename>. A more complex example:
</para>
<para>
<filename>test.nix</filename>:
</para>
<programlisting><xi:include href="./05/test-nix-2.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/test-import-2.txt" parse="text" /></screen>
<para>
Explaining:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
In <filename>test.nix</filename> we return a function. It accepts a set,
with default attributes
<code>b</code>, <code>trueMsg</code> and
<code>falseMsg</code>.
</para>
</listitem>
<listitem>
<para>
<code>builtins.trace</code> is a <link
xlink:href="https://nixos.org/nix/manual/#ssec-builtins">built-in
function</link> that takes two arguments. The first is the message to
display, the second is the value to return. It's usually used for
debugging purposes.
</para>
</listitem>
<listitem>
<para>
Then we import <filename>test.nix</filename>, and call the function with
that set.
</para>
</listitem>
</itemizedlist>
<para>
So when is the message shown? Only when it's in need to be evaluated.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will finally write our first derivation.
</para>
</section>
</chapter>