mirror of
https://github.com/NixOS/nix.git
synced 2025-12-08 18:11:02 +01:00
Merge pull request #14728 from roberth/doc-evaluation-infinite-recursion
doc: Document "evaluation order", some strictness, equality quirk, `||`, `&&`
This commit is contained in:
commit
1d56f413c2
14 changed files with 166 additions and 5 deletions
|
|
@ -74,4 +74,48 @@ in f { x = throw "error"; y = throw "error"; }
|
|||
=> "ok"
|
||||
```
|
||||
|
||||
## Evaluation order
|
||||
|
||||
The order in which expressions are evaluated is generally unspecified, because it does not affect successful evaluation outcomes.
|
||||
This allows more freedom for the evaluator to evolve and to evaluate efficiently.
|
||||
|
||||
Data dependencies naturally impose some ordering constraints: a value cannot be used before it is computed.
|
||||
Beyond these constraints, the evaluator is free to choose any order.
|
||||
|
||||
The order in which side effects such as [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) output occurs is not defined, but may be expected to follow data dependencies. <!-- we may want to be more specific about this. -->
|
||||
|
||||
In a lazy language, evaluation order is often opposite to expectations from strict languages.
|
||||
For example, in `let wrap = x: { wrapped = x; }; in wrap (1 + 2)`, the function body produces a result (`{ wrapped = ...; }`) *before* evaluating `x`.
|
||||
|
||||
## Infinite recursion and stack overflow
|
||||
|
||||
During evaluation, two types of errors can occur when expressions reference themselves or call functions too deeply:
|
||||
|
||||
### Infinite recursion
|
||||
|
||||
This error occurs when a value depends on itself through a cycle, making it impossible to compute.
|
||||
|
||||
```nix
|
||||
let x = x; in x
|
||||
=> error: infinite recursion encountered
|
||||
```
|
||||
|
||||
Infinite recursion happens at the value level when evaluating an expression requires evaluating the same expression again.
|
||||
|
||||
Despite the name, infinite recursion is cheap to compute and does not involve a stack overflow.
|
||||
The cycle is finite and fairly easy to detect.
|
||||
|
||||
### Stack overflow
|
||||
|
||||
This error occurs when the call depth exceeds the maximum allowed limit.
|
||||
|
||||
```nix
|
||||
let f = x: f (x + 1);
|
||||
in f 0
|
||||
=> error: stack overflow; max-call-depth exceeded
|
||||
```
|
||||
|
||||
Stack overflow happens when too many function calls are nested without returning.
|
||||
The maximum call depth is controlled by the [`max-call-depth` setting](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
|
||||
|
||||
[C API]: @docroot@/c-api.md
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
| [Greater than or equal to][Comparison] | *expr* `>=` *expr* | none | 10 |
|
||||
| [Equality] | *expr* `==` *expr* | none | 11 |
|
||||
| Inequality | *expr* `!=` *expr* | none | 11 |
|
||||
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
||||
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
|
||||
| [Logical conjunction] (`AND`) | *bool* `&&` *bool* | left | [12](#precedence-and-disjunctive-normal-form) |
|
||||
| [Logical disjunction] (`OR`) | *bool* <code>\|\|</code> *bool* | left | [13](#precedence-and-disjunctive-normal-form) |
|
||||
| [Logical implication] | *bool* `->` *bool* | right | 14 |
|
||||
| [Pipe operator] (experimental) | *expr* `\|>` *func* | left | 15 |
|
||||
| [Pipe operator] (experimental) | *func* `<\|` *expr* | right | 15 |
|
||||
|
|
@ -162,6 +162,9 @@ Update [attribute set] *attrset1* with names and values from *attrset2*.
|
|||
The returned attribute set will have all of the attributes in *attrset1* and *attrset2*.
|
||||
If an attribute name is present in both, the attribute value from the latter is taken.
|
||||
|
||||
This operator is [strict](@docroot@/language/evaluation.md#strictness) in both *attrset1* and *attrset2*.
|
||||
That means that both arguments are evaluated to [weak head normal form](@docroot@/language/evaluation.md#values), so the attribute sets themselves are evaluated, but their attribute values are not evaluated.
|
||||
|
||||
[Update]: #update
|
||||
|
||||
## Comparison
|
||||
|
|
@ -185,18 +188,95 @@ All comparison operators are implemented in terms of `<`, and the following equi
|
|||
|
||||
## Equality
|
||||
|
||||
- [Attribute sets][attribute set] and [lists][list] are compared recursively, and therefore are fully evaluated.
|
||||
- Comparison of [functions][function] always returns `false`.
|
||||
- [Attribute sets][attribute set] are compared first by attribute names and then by items until a difference is found.
|
||||
- [Lists][list] are compared first by length and then by items until a difference is found.
|
||||
- Comparison of distinct [functions][function] returns `false`, but identical functions may be subject to [value identity optimization](#value-identity-optimization).
|
||||
- Numbers are type-compatible, see [arithmetic] operators.
|
||||
- Floating point numbers only differ up to a limited precision.
|
||||
|
||||
The `==` operator is [strict](@docroot@/language/evaluation.md#strictness) in both arguments; when comparing composite types ([attribute sets][attribute set] and [lists][list]), it is partially strict in their contained values: they are evaluated until a difference is found. <!-- this is woefully underspecified, affecting which expressions evaluate correctly; not just "ordering" or error messages. -->
|
||||
|
||||
### Value identity optimization
|
||||
|
||||
Nix performs equality comparisons of nested values by pointer equality or more abstractly, _identity_.
|
||||
Nix semantics ideally do not assign a unique identity to values as they are created, but equality is an exception to this rule.
|
||||
The disputable benefit of this is that it is more efficient, and it allows cyclical structures to be compared, e.g. `let x = { x = x; }; in x == x` evaluates to `true`.
|
||||
However, as a consequence, it makes a function equal to itself when the comparison is made in a list or attribute set, in contradiction to a simple direct comparison.
|
||||
|
||||
[function]: ./syntax.md#functions
|
||||
|
||||
[Equality]: #equality
|
||||
|
||||
## Logical conjunction
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *bool1* `&&` *bool2*
|
||||
|
||||
Logical AND. Equivalent to `if` *bool1* `then` *bool2* `else false`.
|
||||
|
||||
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> true && false
|
||||
> => false
|
||||
>
|
||||
> false && throw "never evaluated"
|
||||
> => false
|
||||
> ```
|
||||
|
||||
[Logical conjunction]: #logical-conjunction
|
||||
|
||||
## Logical disjunction
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *bool1* `||` *bool2*
|
||||
|
||||
Logical OR. Equivalent to `if` *bool1* `then true` `else` *bool2*.
|
||||
|
||||
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `false`.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> true || false
|
||||
> => true
|
||||
>
|
||||
> true || throw "never evaluated"
|
||||
> => true
|
||||
> ```
|
||||
|
||||
[Logical disjunction]: #logical-disjunction
|
||||
|
||||
### Precedence and disjunctive normal form
|
||||
|
||||
The precedence of `&&` and `||` aligns with disjunctive normal form.
|
||||
Without parentheses, an expression describes multiple "permissible situations" (connected by `||`), where each situation consists of multiple simultaneous conditions (connected by `&&`).
|
||||
|
||||
For example, `A || B && C || D && E` is parsed as `A || (B && C) || (D && E)`, describing three permissible situations: A holds, or both B and C hold, or both D and E hold.
|
||||
|
||||
## Logical implication
|
||||
|
||||
Equivalent to `!`*b1* `||` *b2* (or `if` *b1* `then` *b2* `else true`)
|
||||
> **Syntax**
|
||||
>
|
||||
> *bool1* `->` *bool2*
|
||||
|
||||
Logical implication. Equivalent to `!`*bool1* `||` *bool2* (or `if` *bool1* `then` *bool2* `else true`).
|
||||
|
||||
This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
|
||||
|
||||
> **Example**
|
||||
>
|
||||
> ```nix
|
||||
> true -> false
|
||||
> => false
|
||||
>
|
||||
> false -> throw "never evaluated"
|
||||
> => true
|
||||
> ```
|
||||
|
||||
[Logical implication]: #logical-implication
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
false
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Distinct but not identical functions in attribute set compare as unequal
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#equality
|
||||
{ a = (x: x); } == { a = (x: x); }
|
||||
|
|
@ -0,0 +1 @@
|
|||
true
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Function comparison in attribute set uses value identity optimization
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#value-identity-optimization
|
||||
let
|
||||
f = x: x;
|
||||
in
|
||||
{
|
||||
a = f;
|
||||
} == {
|
||||
a = f;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
false
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Direct comparison of distinct but not identical functions returns false
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#equality
|
||||
(x: x) == (x: x)
|
||||
|
|
@ -0,0 +1 @@
|
|||
false
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Direct comparison of identical function returns false
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#equality
|
||||
let
|
||||
f = x: x;
|
||||
in
|
||||
f == f
|
||||
|
|
@ -0,0 +1 @@
|
|||
false
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Distinct but not identical functions in list compare as unequal
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#equality
|
||||
[ (x: x) ] == [ (x: x) ]
|
||||
|
|
@ -0,0 +1 @@
|
|||
true
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Function comparison in list uses value identity optimization
|
||||
# See https://nix.dev/manual/nix/latest/language/operators#value-identity-optimization
|
||||
let
|
||||
f = x: x;
|
||||
in
|
||||
[ f ] == [ f ]
|
||||
Loading…
Add table
Add a link
Reference in a new issue