diff --git a/src/tutorials/lang/QuickOverview.md b/src/tutorials/lang/QuickOverview.md index bc406b2d..ff647ae6 100644 --- a/src/tutorials/lang/QuickOverview.md +++ b/src/tutorials/lang/QuickOverview.md @@ -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; } ``` - -::: note -元素的声明顺序并不决定元素在属性集中的排布顺序,属性集中的元素排布顺序是由求值顺 -序决定的,优先被求值的被放在了前面。 +可以看到,结果中的 `a = 1` 在前面,`b = 2` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与**属性名称本身的排序**有关。例如,对 `rec { a = 1; b = 2; }` 与 `rec { b = 2; a = 1; }` 的求值,都会把 `a = 1` 放在前面,归因到底,这只是因为 `a` 在字母表中位于 `b` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。) - -::: +既然如此,将上面属性集里的两个元素位置对调: +```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` 绑定