diff --git a/source/nix-language/attribute-sets.adoc b/source/nix-language/attribute-sets.adoc index 1493c0a..da15e52 100644 --- a/source/nix-language/attribute-sets.adoc +++ b/source/nix-language/attribute-sets.adoc @@ -1,5 +1,45 @@ = Attribute set operations +== Selecttion + +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" +.... + +== Query + +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 +.... + +== Modification + +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"; } +.... + +== Recursive attribute sets An ordinary attribute set cannot refer to its own elements. To do this, you need a _recursive_ attribute set. @@ -17,37 +57,3 @@ error: undefined variable 'x' nix-repl> rec { x = 3; y = 4*x; } { x = 3; y = 12; } .... - - -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 index 0a69612..a0c404d 100644 --- a/source/nix-language/booleans.adoc +++ b/source/nix-language/booleans.adoc @@ -1,4 +1,4 @@ -# Boolean operations += Boolean operations The usual boolean operators are available. Recall that earlier we set `a = 7` and `b = 3`. diff --git a/source/nix-language/functions.adoc b/source/nix-language/functions.adoc index fad1d51..3010b7a 100644 --- a/source/nix-language/functions.adoc +++ b/source/nix-language/functions.adoc @@ -1,3 +1,123 @@ = Functions -// TODO +== Anonymous functions + +Functions are defined using the syntax `_parameter_: _expression_`, +where the _expression_ typically involves the _parameter_. +Consider the following example. + +[source] +.... +nix-repl> x: x + 1 +«lambda @ «string»:1:1» +.... + +We created a function that adds `1` to its input. +However, it doesn't have a name, so we can't use it directly. +Anonymous functions do have their uses, as we shall see shortly. + +Note that the message printed by the NixREPL when we created the function uses the term _lambda_. +This derives from a branch of mathematics called _lambda calculus_. +Lambda calculus was the inspiration for most functional languages such as Nix. +Functional programmers often call anonymous functions "lambdas". + +The Nix REPL confirms that the expression `x: x + 1` defines a function. + +[source] +.... +nix-repl> :t x: x + 1 +a function +.... + +== Named functions and function application + +How can we use a function? +Recall from <> that functions can be treated like any other data type. +In particular, we can assign it to a variable. + +[source] +.... +nix-repl> f = x: x + 1 + +nix-repl> f +«lambda @ «string»:1:2» +.... + +Procedural languages such as C or Java often use parenthesis to apply a function to a value, e.g. `f(5)`. +Nix, like lambda calculus and most functional languages, does not require parenthesis for function application. +This reduces visual clutter when chaining a series of functions. + +Now that our function has a name, we can use it. + +[source] +.... +nix-repl> f 5 +6 +.... + +== Multiple parameters + +Functions in Nix always have a single parameter. +To define a calculation that requires more than one parameter, +we create functions that return functions! + +[source] +.... +nix-repl> add = a: (b: a+b) +.... + +We have created a function called `add`. +When applied to a parameter `a`, it returns a new function that adds `a` to its input. +Note that the expression `(b: a+b)` is an anonymous function. +We never call it directly, so it doesn't need a name. +Anonymous functions are useful after all! + +[source] +.... +nix-repl> add 3 # Returns a function that adds 3 to any input +«lambda @ «string»:1:6» +.... + +Now let's apply `add 3` to the value `5`. + +[source] +.... +nix-repl> (add 3) 5 +8 +.... + +In fact, the parentheses aren't needed. + +[source] +.... +nix-repl> add 3 5 +8 +.... + +In case that wasn't clear, let's repeat those steps, but in more detail. +The function `add` takes a single parameter `a`, +and returns a new function that takes a single parameter `b`, and returns the value `a + b`. +Let's apply `add` to the value `3`, and give the resulting new function a name, `g`. + +[source] +.... +nix-repl> g = add 3 +.... + +The function `g` takes a single parameter and adds `3` to it. +The Nix REPL confirms that `g` is indeed a function. + +[source] +.... +nix-repl> :t g +a function +.... + +Now we can apply `g` to a number to get a new number. + +[source] +.... +nix-repl> g 5 +8 +.... + diff --git a/source/nix-language/let.adoc b/source/nix-language/let.adoc index ceff96a..5590fe8 100644 --- a/source/nix-language/let.adoc +++ b/source/nix-language/let.adoc @@ -44,3 +44,5 @@ This is similar to how recursive attribute sets work. nix-repl> let x = 3; y = x + 1; in x*y 12 .... + +// TODO Discuss inheriting attributes https://nixos.org/manual/nix/stable/language/constructs#inheriting-attributes diff --git a/source/nix-language/main.adoc b/source/nix-language/main.adoc index d59f496..d5bdc67 100644 --- a/source/nix-language/main.adoc +++ b/source/nix-language/main.adoc @@ -31,3 +31,5 @@ include::if.adoc[leveloffset=+1] include::let.adoc[leveloffset=+1] include::with.adoc[leveloffset=+1] + +// TODO discuss laziness diff --git a/source/nix-language/numbers.adoc b/source/nix-language/numbers.adoc index 58a4f70..d44cc91 100644 --- a/source/nix-language/numbers.adoc +++ b/source/nix-language/numbers.adoc @@ -1,4 +1,6 @@ -# Numeric operations += Numeric operations + +== Arithmetic operators The usual arithmetic operators are provided. @@ -22,8 +24,9 @@ nix-repl> -1 # negation [IMPORTANT] ==== -The spaces before and after operators aren't always required. -However, you can get unexpected results when you omit them, as shown below. +As mentioned in <<_variables>>, +you can get unexpected results when you omit spaces around operators. +Consider the following example. [source] .... @@ -37,6 +40,7 @@ Let's use the `:t` command to find out the type of the expression. [source] .... nix-repl> :t 6/2 +a path .... If an expression can be interpreted as a path, Nix will do so. @@ -55,6 +59,8 @@ nix-repl> 6/ 2 To avoid surprises and improve readability, I prefer to use spaces before and after all operators. ==== +== Floating-point calculations + 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. diff --git a/source/nix-language/paths.adoc b/source/nix-language/paths.adoc index 171dabb..c461574 100644 --- a/source/nix-language/paths.adoc +++ b/source/nix-language/paths.adoc @@ -14,7 +14,9 @@ nix-repl> ./. /home/amy/codeberg/nix-book .... -Paths can be concatenated with each other to produce a new path. +== Concatenating paths + +Paths can be concatenated to produce a new path. [source] .... @@ -27,8 +29,8 @@ 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`. +Relative paths are made absolute when they are parsed, which occurs before concatenation. +This is why the result in the example below is not `/home/wombat/file.txt`. [source] .... @@ -37,15 +39,16 @@ nix-repl> /home/wombat + ./file.txt .... ==== +== Concatenating a path + a string A path can be concatenated with a string to produce a new path. [source] .... -nix-repl> homePath + "/file.txt" +nix-repl> /home/wombat + "/file.txt" /home/wombat/file.txt -nix-repl> :t homePath + "/myfile.txt" +nix-repl> :t /home/wombat + "/file.txt" a path .... @@ -56,12 +59,14 @@ String contexts are beyond the scope of this book; for more information see https://nixos.org/manual/nix/stable/language/operators#path-concatenation. ==== +== Concatenating a string + a path + 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`). +(not, as you might expect, to `/home/wombat/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt`). [source] .... diff --git a/source/nix-language/strings.adoc b/source/nix-language/strings.adoc index 67cc7fb..5e61fe4 100644 --- a/source/nix-language/strings.adoc +++ b/source/nix-language/strings.adoc @@ -1,4 +1,6 @@ -# String operations += String operations + +== String concatenation String concatenation uses the `+` operator. @@ -8,6 +10,8 @@ nix-repl> "Hello, " + "world!" "Hello, world!" .... +== String interpolation + You can use the `${_variable_}` syntax to insert the value of a variable within a string. [source] @@ -21,7 +25,6 @@ nix-repl> "Hi, I'm ${name}." [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] @@ -37,6 +40,5 @@ error: error: cannot coerce an integer to a string .... +Nix does provide functions for converting between types; we'll see these in <<_built_in_functions>>. ==== - -// 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 index ed3e5ac..d49bf53 100644 --- a/source/nix-language/types.adoc +++ b/source/nix-language/types.adoc @@ -1,5 +1,6 @@ = Data types +[#type-string] == Strings Strings are enclosed by double quotes (`"`), or _two_ single quotes (`'`). @@ -15,20 +16,24 @@ They can span multiple lines. The sound of water -- Basho'' +[#type-integer] == Integers 7 256 +[#type-float] == Floating point numbers 3.14 6.022e23 +[#type-boolean] == Boolean The Boolean values in Nix are `true` and `false`. +[#type-path] == Paths File paths are play an important role in building software, so Nix has a special data type for them. @@ -40,6 +45,7 @@ listed in the environment variable NIX_PATH to be searched for the given file or directory name. These are called _lookup paths_. +[#type-list] == Lists List elements are enclosed in square brackets and separated by spaces (not commas). @@ -55,6 +61,7 @@ List elements can be of any type, and can even be lists themselves. [ [ 1 2 ] [ 3 4 ] ] +[#type-set] == Attribute sets Attribute sets associate keys with values. @@ -76,12 +83,15 @@ Values of attribute sets can be of any type, and can even be attribute sets them { name = { first = "Professor"; last = "Paws"; }; age = 10; species = "cat"; } +In <<_recursive_attribute_sets>> you will be introduced to a special type of attribute set. + [NOTE] ==== In some Nix documentation, and in many articles about Nix, attribute sets are simply called "sets". ==== +[#type-lambda] == Functions We'll learn how to write functions later in this chapter. diff --git a/source/nix-language/variables.adoc b/source/nix-language/variables.adoc index fabd304..f140c28 100644 --- a/source/nix-language/variables.adoc +++ b/source/nix-language/variables.adoc @@ -1,7 +1,10 @@ = Variables +// TODO what characters are legal in a variable name/identifier? + +== Assignment + You can declare variables in Nix and assign values to them. -// TODO explain that values are immutable. [source] .... @@ -15,9 +18,10 @@ nix-repl> a - b [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. +The spaces before and after operators aren't always required. +However, you can get unexpected results when you omit them, as shown in the following example. +Nix allows hyphens (`-`) in variable names, +so `a-b` is interpreted as the name of a variable rather than a subtraction operation. [source] .... @@ -31,3 +35,33 @@ error: undefined variable 'a-b' .... ==== +== Immutability + +In Nix, values are _immutable_; +once you assign a value to a variable, you cannot change it. +You can, however, create a new variable with the same name, but in a different scope. +Don't worry if you don't completely understand the previous sentence; +we will see some examples in <<_functions>>, <<_let_expressions>>, and <<_with_expressions>>. + +[IMPORTANT] +==== +In the Nix REPL, it may seem like the values of variables can be changed, +in _apparent_ contradiction to the previous paragraph. +In truth, the REPL works some behind-the-scenes "magic", +effectively creating a new nested scope with each assignment. +This makes it much easier to experiment in the REPL. + +[source] +.... +nix-repl> x = 1 + +nix-repl> x +1 + +nix-repl> x = x + 1 # creates a new variable called "x"; the original is no longer in scope + +nix-repl> x +2 +.... +==== + diff --git a/source/nix-language/with.adoc b/source/nix-language/with.adoc index 4e97c71..5f93e46 100644 --- a/source/nix-language/with.adoc +++ b/source/nix-language/with.adoc @@ -1,4 +1,4 @@ -= With expression += With expressions A `with` expression is somewhat similar to a `let` expression, but it brings all of the associations in an attribute set into scope.