added lang chapter

This commit is contained in:
Amy de Buitléir 2023-12-02 15:29:29 +00:00
parent 6a7c98ed4a
commit e43734eba8
11 changed files with 241 additions and 54 deletions

View file

@ -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"; }
....

View file

@ -1,4 +1,4 @@
# Boolean operations
= Boolean operations
The usual boolean operators are available.
Recall that earlier we set `a = 7` and `b = 3`.

View file

@ -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 <<type-lambda>> 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
....

View file

@ -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

View file

@ -31,3 +31,5 @@ include::if.adoc[leveloffset=+1]
include::let.adoc[leveloffset=+1]
include::with.adoc[leveloffset=+1]
// TODO discuss laziness

View file

@ -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.

View file

@ -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]
....

View file

@ -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/

View file

@ -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.

View file

@ -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
....
====

View file

@ -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.