Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 49 additions & 12 deletions src/tutorials/lang/QuickOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,29 +260,66 @@ foo.b.d = 3;

## 递归属性集

当属性集内的属性需要访问该集合的另一个属性时,应当使用递归属性集:
普通的属性集不支持递归引用,举个例子:
```nix
{
a = 1;
b = a + 1;
}
```
对上面的表达式求值,会报错:
```
error: undefined variable 'a'
```
可见,当属性集内的属性 `b` 需要访问该集合的另一个属性 `a` 时,即使 `a` 是“先”定义的,也无法访问到。此时就需要我们改用递归(recursive)属性集,它相比普通的属性集,在前面多加了 `rec `:

```nix
rec {
one = 1;
two = one + 1; # 直接依赖于 one
three = two + 1; # 直接依赖于 two,间接依赖于 one
a = 1;
b = a * 2 + 1;
}
```

输出如下
对上面的表达式求值,结果如下

```nix
{ one = 1; three = 3; two = 2; }
{ a = 1; b = 3; }
```

<!-- prettier-ignore -->
::: note
元素的声明顺序并不决定元素在属性集中的排布顺序,属性集中的元素排布顺序是由求值顺
序决定的,优先被求值的被放在了前面。
可以看到,结果中的 `a = 1` 在前面,`b = 2` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与**属性名称本身的排序**有关。例如,对 `rec { a = 1; b = 2; }` 与 `rec { b = 2; a = 1; }` 的求值,都会把 `a = 1` 放在前面,归因到底,这只是因为 `a` 在字母表中位于 `b` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。)

<!-- prettier-ignore -->
:::
既然如此,将上面属性集里的两个元素位置对调:
```nix
rec {
b = a * 2 + 1;
a = 1;
}
```
你会发现,Nix 也能自动处理求值顺序,并不会因为 `a` 的声明被调整到后面而影响求值结果(与之前的完全一致,这里就不贴了)。这看起来相当“智能”,你甚至可以写得更复杂一些,比如 Nix 也能自动处理下面的例子(结果略):
```nix
rec {
c = a * 2 - b + d - 35;
a = 12;
b = d * 2 + 64;
d = a - 15;
}
```

不过,这并不代表你可以直接用它来解方程。例如我们再写一个在数学上有唯一解的方程组:
```nix
rec {
b = a * 2 + 1;
a = b + 1;
}
```
此表达式求值的输出如下:
```nix
{
a = «error: infinite recursion encountered»;
b = «error: infinite recursion encountered»;
}
```
从这个输出来看,我们可以发现递归属性集内部在处理求值顺序的机制,确实是递归的,而如果递归陷入死循环就会报错。

## `let` 绑定

Expand Down