From 3bda18c1db1b3890cb495307c7dbefbbfdbf2762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amy=20de=20Buitl=C3=A9ir?= Date: Fri, 1 Dec 2023 18:32:34 +0000 Subject: [PATCH] initial commit --- source/nix-language/attribute-sets.adoc | 34 ++++++++++ source/nix-language/booleans.adoc | 50 ++++++++++++++ source/nix-language/builtins.adoc | 6 ++ source/nix-language/dont-read.adoc | 31 +++++++++ source/nix-language/functions.adoc | 3 + source/nix-language/intro.adoc | 16 +++++ source/nix-language/lists.adoc | 9 +++ source/nix-language/main.adoc | 27 ++++++++ source/nix-language/numbers.adoc | 83 +++++++++++++++++++++++ source/nix-language/paths.adoc | 65 ++++++++++++++++++ source/nix-language/repl.adoc | 44 ++++++++++++ source/nix-language/strings.adoc | 42 ++++++++++++ source/nix-language/types.adoc | 90 +++++++++++++++++++++++++ source/nix-language/variables.adoc | 33 +++++++++ 14 files changed, 533 insertions(+) create mode 100644 source/nix-language/attribute-sets.adoc create mode 100644 source/nix-language/booleans.adoc create mode 100644 source/nix-language/builtins.adoc create mode 100644 source/nix-language/dont-read.adoc create mode 100644 source/nix-language/functions.adoc create mode 100644 source/nix-language/intro.adoc create mode 100644 source/nix-language/lists.adoc create mode 100644 source/nix-language/main.adoc create mode 100644 source/nix-language/numbers.adoc create mode 100644 source/nix-language/paths.adoc create mode 100644 source/nix-language/repl.adoc create mode 100644 source/nix-language/strings.adoc create mode 100644 source/nix-language/types.adoc create mode 100644 source/nix-language/variables.adoc diff --git a/source/nix-language/attribute-sets.adoc b/source/nix-language/attribute-sets.adoc new file mode 100644 index 0000000..9a93b43 --- /dev/null +++ b/source/nix-language/attribute-sets.adoc @@ -0,0 +1,34 @@ += Attribute set operations + +The `.` operator selects an attribute from a set. + +[source] +.... +nix-repl> animal = { name = { first = "Professor"; last = "Paws"; }; age = 10; species = "cat"; } + +nix-repl> animal . age +10 + +nix-repl> animal . name . first +"Professor" +.... + +We can use the `?` operator to find out if a set has a particular attribute. + +[source] +.... +nix-repl> animal ? species +true + +nix-repl> animal ? bicycle +false +.... + +We can use the `//` operator to modify an attribute set. +Recall that Nix values are immutable, so the result is a new value (the original is not modified). + +[source] +.... +nix-repl> animal // { species = "tiger"; } +{ age = 10; name = { ... }; species = "tiger"; } +.... diff --git a/source/nix-language/booleans.adoc b/source/nix-language/booleans.adoc new file mode 100644 index 0000000..0a69612 --- /dev/null +++ b/source/nix-language/booleans.adoc @@ -0,0 +1,50 @@ +# Boolean operations + +The usual boolean operators are available. +Recall that earlier we set `a = 7` and `b = 3`. + +[source] +.... +nix-repl> a == 7 # equality test +true + +nix-repl> b != 3 # inequality +false + +nix-repl> a > 12 # greater than +false + +nix-repl> b >= 2 # greater than or equal +true + +nix-repl> a < b # less than +false + +nix-repl> b <= a # less than or equal +true + +nix-repl> !true # logical negation +false + +nix-repl> (3 * a == 21) && (a > b) # logical AND +true + +nix-repl> (b > a) || (b > 10) # logical OR +false +.... + +One operator that might be unfamiliar to you is _logical implication_, which uses the symbol `->`. +The expression `u -> v` is equivalent to `!u || v`. + +[source] +.... +nix-repl> u = false + +nix-repl> v = true + +nix-repl> u -> v +true + +nix-repl> v -> u +false +.... diff --git a/source/nix-language/builtins.adoc b/source/nix-language/builtins.adoc new file mode 100644 index 0000000..d960bec --- /dev/null +++ b/source/nix-language/builtins.adoc @@ -0,0 +1,6 @@ += Built-in functions + +// TODO + +For a complete list of built-in functions, see +https://nixos.org/manual/nix/stable/language/builtins diff --git a/source/nix-language/dont-read.adoc b/source/nix-language/dont-read.adoc new file mode 100644 index 0000000..9f84d68 --- /dev/null +++ b/source/nix-language/dont-read.adoc @@ -0,0 +1,31 @@ += Stop reading this chapter! + +When I first began using Nix, it seemed logical to start by learning the Nix language. +However, after following an in-depth tutorial, +I found that I didn't know how to do anything useful with the language! +It wasn't until later that I understood what I was missing: +a guide to the most useful library functions. + +When working with Nix or NixOS, +it's very rare that you'll want to write something from scratch. +Instead, you'll use one of the many library functions +that make things easier and shield you from the underlying complexity. +Many of these functions are language-specific, +and the documentation for them may be inadequate. +Often the easiest (or only) way to learn to use them +is to find an example that does something similar to what you want, +and then modify the function parameters to suit your needs. + +At this point you've learned enough of the Nix language +to do the majority of common Nix tasks. +So when I say "Stop reading this chapter!", I'm only half-joking. +Instead I suggest that you _skim_ the rest of this chapter, +paying special attention to anything marked with icon:exclamation-circle[]. +Then move on to the following chapters +where you will learn how to develop and package software using Nix. +Afterward, come back to this chapter and read it in more detail. + +While writing this book, I anticipated that readers would want to skip around, +alternating between pure learning and learning-by-doing. +I've tried to structure the book to support that; +sometimes repeating information from earlier chapters that you might have skipped. diff --git a/source/nix-language/functions.adoc b/source/nix-language/functions.adoc new file mode 100644 index 0000000..fad1d51 --- /dev/null +++ b/source/nix-language/functions.adoc @@ -0,0 +1,3 @@ += Functions + +// TODO diff --git a/source/nix-language/intro.adoc b/source/nix-language/intro.adoc new file mode 100644 index 0000000..556242c --- /dev/null +++ b/source/nix-language/intro.adoc @@ -0,0 +1,16 @@ += Introducing the Nix language + +Nix and NixOS use a functional programming language called _Nix_ +to specify how to build and install software, +and how to configure system, user, and project-specific environments. +(Yes, “Nix” is the name of both the package manager and the language it uses.) + +Nix is a _functional_ language. +In a _procedural_ language such as C or Java, +the focus is on writing a series of _steps_ (statements) to achieve a desired result. +By contrast, in a functional language the focus is on _defining_ the desired result. + +In the case of Nix, the desired result is usually a _derivation_: +a software package that has been built and is ready for use. +The Nix language has been designed for that purpose, +and thus has some features you don't typically find in general-purpose languages. diff --git a/source/nix-language/lists.adoc b/source/nix-language/lists.adoc new file mode 100644 index 0000000..5b8a272 --- /dev/null +++ b/source/nix-language/lists.adoc @@ -0,0 +1,9 @@ += List operations + +Lists can be concatenated using the `++` operator. + +[source] +.... +nix-repl> [ 1 2 3 ] ++ [ "apple" "banana" ] +[ 1 2 3 "apple" "banana" ] +.... diff --git a/source/nix-language/main.adoc b/source/nix-language/main.adoc new file mode 100644 index 0000000..f0ae79b --- /dev/null +++ b/source/nix-language/main.adoc @@ -0,0 +1,27 @@ += The Nix language + +include::intro.adoc[leveloffset=+1] + +include::types.adoc[leveloffset=+1] + +include::dont-read.adoc[leveloffset=+1] + +include::repl.adoc[leveloffset=+1] + +include::variables.adoc[leveloffset=+1] + +include::numbers.adoc[leveloffset=+1] + +include::strings.adoc[leveloffset=+1] + +include::booleans.adoc[leveloffset=+1] + +include::paths.adoc[leveloffset=+1] + +include::lists.adoc[leveloffset=+1] + +include::attribute-sets.adoc[leveloffset=+1] + +include::functions.adoc[leveloffset=+1] + +include::builtins.adoc[leveloffset=+1] diff --git a/source/nix-language/numbers.adoc b/source/nix-language/numbers.adoc new file mode 100644 index 0000000..58a4f70 --- /dev/null +++ b/source/nix-language/numbers.adoc @@ -0,0 +1,83 @@ +# Numeric operations + +The usual arithmetic operators are provided. + +[source] +.... +nix-repl> 1 + 2 # addition +3 + +nix-repl> 5 - 3 # subtraction +2 + +nix-repl> 3 * 4 # multiplication +12 + +nix-repl> 6 / 2 # division +3 + +nix-repl> -1 # negation +-1 +.... + +[IMPORTANT] +==== +The spaces before and after operators aren't always required. +However, you can get unexpected results when you omit them, as shown below. + +[source] +.... +nix-repl> 6/2 +/home/amy/codeberg/nix-book/6/2 +.... + +What happened? +Let's use the `:t` command to find out the type of the expression. + +[source] +.... +nix-repl> :t 6/2 +.... + +If an expression can be interpreted as a path, Nix will do so. +This is useful, because paths are _far_ more commonly used in Nix expressions that arithmetic operators. +In this case, Nix interpreted `6/2` as a relative path from the current directory, +which in the above example was `/home/amy/codeberg/nix-book`. + +Adding a space after the `/` operator produces the expected result. + +[source] +.... +nix-repl> 6/ 2 +3 +.... + +To avoid surprises and improve readability, I prefer to use spaces before and after all operators. +==== + +Numbers without a decimal point are assumed to be integers. +To ensure that a number is interpreted as a floating-point value, add a decimal point. + +[source] +.... +nix-repl> :t 5 +an integer + +nix-repl> :t 5.0 +a float + +nix-repl> :t 5. +a float +.... + +In the example below, the first expression results in integer division (rounding down), +while the second produces a floating-point result. + +[source] +.... +nix-repl> 5 / 3 +1 + +nix-repl> 5.0 / 3 +1.66667 +.... diff --git a/source/nix-language/paths.adoc b/source/nix-language/paths.adoc new file mode 100644 index 0000000..805f00d --- /dev/null +++ b/source/nix-language/paths.adoc @@ -0,0 +1,65 @@ += Path opearations + +Paths can be concatenated with each other to produce a new path. + +[source] +.... +nix-repl> /home/wombat + /bin/sh +/home/wombat/bin/sh + +nix-repl> :t /home/wombat + /bin/sh +a path +.... + +[IMPORTANT] +==== +Relative paths become absolute when they are parsed, which occurs before concatenation. +This is why the result in the example below is not `/home/amy/file.txt`. + +[source] +.... +nix-repl> /home/wombat + ./file.txt +/home/wombat/home/amy/codeberg/nix-book/file.txt +.... +==== + + +A path can be concatenated with a string to produce a new path. + +[source] +.... +nix-repl> homePath + "/file.txt" +/home/wombat/file.txt + +nix-repl> :t homePath + "/myfile.txt" +a path +.... + +[NOTE] +==== +The Nix reference manual says that the string must not "have a string context" that refers to a store path. +String contexts are beyond the scope of this book; +for more information see https://nixos.org/manual/nix/stable/language/operators#path-concatenation. +==== + +Strings can be concatenated with paths, but with a side-effect that may surprise you: +if the path exists, the file is copied to the Nix store! +The result is a string, not a path. + +In the example below, the file `file.txt` is copied to `/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt` +(not `/home/wombat/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt`). + +[source] +.... +nix-repl> "/home/wombat" + ./file.txt +"/home/wombat/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt" +.... + +The path must exist. + +[source] +.... +nix-repl> "/home/wombat" + ./no-such-file.txt +error (ignored): error: end of string reached +error: getting status of '/home/amy/codeberg/nix-book/no-such-file.txt': No such file or directory +.... diff --git a/source/nix-language/repl.adoc b/source/nix-language/repl.adoc new file mode 100644 index 0000000..ff47b17 --- /dev/null +++ b/source/nix-language/repl.adoc @@ -0,0 +1,44 @@ += The Nix REPL + +The Nix REPL footnote:[REPL is an acronym for (Read-Eval-Print-Loop).] +is an interactive environment for evaluating and debugging Nix code. +It's also a good place to begin learning Nix. +Enter it using the command `nix repl`. +Within the REPL, type `:?` to see a list of available commands. + +[source] +.... +$# echo "$ nix repl" +Welcome to Nix 2.18.1. Type :? for help. + +nix-repl> :? +The following commands are available: + + Evaluate and print expression + = Bind expression to variable + :a, :add Add attributes from resulting set to scope + :b Build a derivation + :bl Build a derivation, creating GC roots in the + working directory + :e, :edit Open package or function in $EDITOR + :i Build derivation, then install result into + current profile + :l, :load Load Nix expression and add it to scope + :lf, :load-flake Load Nix flake and add it to scope + :p, :print Evaluate and print expression recursively + :q, :quit Exit nix-repl + :r, :reload Reload all files + :sh Build dependencies of derivation, then start + nix-shell + :t Describe result of evaluation + :u Build derivation, then start nix-shell + :doc Show documentation of a builtin function + :log Show logs for a derivation + :te, :trace-enable [bool] Enable, disable or toggle showing traces for + errors + :?, :help Brings up this help menu +.... + +A command that is useful to beginners is `:t`, which tells you the type of an expression. + +Note that the command to exit the REPL is `:q` (or `:quit` if you prefer). diff --git a/source/nix-language/strings.adoc b/source/nix-language/strings.adoc new file mode 100644 index 0000000..67cc7fb --- /dev/null +++ b/source/nix-language/strings.adoc @@ -0,0 +1,42 @@ +# String operations + +String concatenation uses the `+` operator. + +[source] +.... +nix-repl> "Hello, " + "world!" +"Hello, world!" +.... + +You can use the `${_variable_}` syntax to insert the value of a variable within a string. + +[source] +.... +nix-repl> name = "Wombat" + +nix-repl> "Hi, I'm ${name}." +"Hi, I'm Wombat." +.... + +[IMPORTANT] +==== +You cannot mix numbers and strings. +Nix does provide functions for converting between types; we'll see these later. +Earlier we set `a = 7`, so the following expression fails. + +[source] +.... +nix-repl> "My favourite number is ${a}." +error: + … while evaluating a path segment + + at «string»:1:25: + + 1| "My favourite number is ${a}." + | ^ + + error: cannot coerce an integer to a string +.... +==== + +// TODO Talk about "strings with context" https://shealevy.com/blog/2018/08/05/understanding-nixs-string-context/ diff --git a/source/nix-language/types.adoc b/source/nix-language/types.adoc new file mode 100644 index 0000000..695f555 --- /dev/null +++ b/source/nix-language/types.adoc @@ -0,0 +1,90 @@ += Data types + +== Strings + +Strings are enclosed by double quotes (`"`), or _two_ single quotes (`'`). + + "Hello, world!" + + ''This string contains "double quotes"'' + +They can span multiple lines. + + ''Old pond + A frog jumps in + The sound of water + -- Basho'' + +== Integers + + 7 + 256 + +== Floating point numbers + + 3.14 + 6.022e23 + +== Boolean + +The Boolean values in Nix are `true` and `false`. + +== Paths + +File paths are play an important role in building software, so Nix has a special data type for them. +Paths may be absolute (e.g. `/bin/sh`) or relative (e.g. `./data/file1.csv`). +Note that paths are not enclosed in quotation marks; they are not strings! + +Enclosing a path in angle brackets, e.g. causes the directories +listed in the environment variable NIX_PATH to be searched for the given +file or directory name. +These are called _lookup paths_. + +== Lists + +List elements are enclosed in square brackets and separated by spaces (not commas). +The elements need not be of the same type. + + [ "apple" 123 ./build.sh false ] + +Lists can be empty. + + [] + +List elements can be of any type, and can even be lists themselves. + + [ [ 1 2 ] [ 3 4 ] ] + +== Attribute sets + +Attribute sets associate keys with values. +They are enclosed in curly brackets, and the associations are terminated by semi-colons. +Note that the final semi-colon before the closing bracket is required. + + { name = "Professor Paws"; age = 10; species = "cat"; } + + +Attribute sets can be empty. + + {} + +Values of attribute sets can be of any type, and can even be attribute sets themselves. + + { name = { first = "Professor"; last = "Paws"; }; age = 10; species = "cat"; } + +[NOTE] +==== +In some Nix documentation, and in many articles about Nix, +attribute sets are simply called "sets". +==== + +== Functions + +We'll learn how to write functions later in this chapter. +For now, note that functions are "first-class values", +meaning that they can be treated like any other data type. +For example, a function can be assigned to a variable, appear as an element in a list, +or be associated with a key in an attribute set. + + [ "apple" 123 ./build.sh false (x: x*x) ] + { name = "Professor Paws"; age = 10; species = "cat"; formula = (x: x*2); } diff --git a/source/nix-language/variables.adoc b/source/nix-language/variables.adoc new file mode 100644 index 0000000..fabd304 --- /dev/null +++ b/source/nix-language/variables.adoc @@ -0,0 +1,33 @@ += Variables + +You can declare variables in Nix and assign values to them. +// TODO explain that values are immutable. + +[source] +.... +nix-repl> a = 7 + +nix-repl> b = 3 + +nix-repl> a - b +4 +.... + +[IMPORTANT] +==== +As mentioned previously, omitting the spaces around operators can have unexpected results. +As Nix allows hyphens (`-`) in variable names, +`a-b` is interpreted as the name of a variable in the following example. + +[source] +.... +nix-repl> a-b +error: undefined variable 'a-b' + + at «string»:1:1: + + 1| a-b + | ^ +.... +==== +