mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2026-05-12 06:45:53 +08:00
added lang chapter
This commit is contained in:
parent
6a7c98ed4a
commit
e43734eba8
11 changed files with 241 additions and 54 deletions
|
|
@ -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"; }
|
||||
....
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Boolean operations
|
||||
= Boolean operations
|
||||
|
||||
The usual boolean operators are available.
|
||||
Recall that earlier we set `a = 7` and `b = 3`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
....
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,3 +31,5 @@ include::if.adoc[leveloffset=+1]
|
|||
include::let.adoc[leveloffset=+1]
|
||||
|
||||
include::with.adoc[leveloffset=+1]
|
||||
|
||||
// TODO discuss laziness
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
....
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
....
|
||||
====
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue