1010<!-- prettier-ignore -->
1111:::
1212
13+ <details ><summary >仅面向本文维护者的说明,单击以切换折叠/展开</summary >
14+ 本文在设计上是线性的,也即只需要读者具备一点点基础,就可以通过按顺序从头读到尾的方式完成本文的学习。因此,请留心说明顺序,例如讲 let 绑定时如果举了一个列表的例子,你需要确保前面已经正式介绍过列表。再如,讲 with 语法糖的时候同时用到 let 绑定和列表,那么这两个概念都需要在前面已经正式介绍过。否则,读者很可能会面对初次接触的语法或者概念而被卡住,这会严重影响学习效率甚至是完成率。若出于顺序安排的其他合理性原因,实在无法避开在说明中涉及陌生概念,可以提示读者相关部分不需要理解,后面会讲到。
15+ </details >
16+
1317Nix 作为语言,是一门简单的函数式语言,它被专门设计并用于 Nix 包管理器及相关生态
1418(NixOS、Home-Manager 等)。
1519
@@ -155,7 +159,39 @@ let a = builtins.div 2 0; b = 3; in b
155159
156160** 好了,下面正式介绍 Nix 语法。**
157161
158- ## 名称和值
162+ ## 注释、缩进与换行
163+ 注释、缩进与换行的语法与机制,对编程语言的风格有重要影响。本节将介绍 Nix 语言中的注释、缩进与换行。
164+
165+ - 注释:在 Nix 语言中,用 ` # ` 表示注释,在它之后直到行末的部分都会被忽略。
166+ - 缩进与换行:与 Python 这种对缩进有要求的语言不同,在 Nix 语言中,大多数情况下,换行与缩进只是为了更好的可读性,并不影响代码的本质。
167+
168+ 例如,下面有两段示例代码(你目前还不需要理解它们的含义),它们在本质上(也即在 Nix 解释器看来)并没有区别。
169+
170+ 第一例:
171+ ``` nix
172+ foo = { a = 1; b = 2; }
173+ ```
174+
175+ 第二例:
176+ ``` nix
177+ foo = { # 这是一句注释,放在代码末尾。
178+ # 这也是一句注释,单独占了一行。
179+ a = 1; # 这里即使不缩进,也不影响代码本质。
180+ b = 2;
181+ # c = 3; # 这里的代码被注释掉了,相当于不存在。
182+ }
183+ ```
184+
185+ <!-- prettier-ignore -->
186+ ::: warning
187+ 换行与空格一样具有分隔作用,请勿在不可分隔的地方胡乱断行。
188+
189+ <!-- prettier-ignore -->
190+ :::
191+
192+ ## 赋值与集合
193+ 与大多数编程语言类似,变量与值是 Nix 语言中最基础的概念。本节将会介绍 Nix 中如何为变量** 赋值** ,以及最常用的数据类型——** 属性集** ,继而引出** 递归属性集** 与** 列表** 的概念。
194+ ### 名称和值
159195
160196我们可以使用 ` = ` 为名称绑定值,形成赋值语句。例如将名称 ` foo ` 赋值为 ` 123 ` :
161197
@@ -164,9 +200,10 @@ foo = 123
164200```
165201
166202<!-- prettier-ignore -->
167- ::: info 名称与变量的区别
168- 名称不等同常见编程语言中的变量。
169- 传统的赋值会改变变量的状态,而 Nix 语言中的名称一旦赋值(定义)就无法改变。
203+ ::: info 名称与其它语言中变量的区别
204+ 在很多语言中,赋值会改变变量的状态,而 Nix 语言中的名称一旦赋值(定义)就无法改变。
205+
206+ 不过,将 Nix 中的名称称为变量也没问题。
170207
171208<!-- prettier-ignore -->
172209:::
@@ -182,7 +219,7 @@ foo = 123
182219- 列表(list),例如 ` [ 1 "tux" false ] `
183220- 属性集(attribute set),例如 ` { a = 1; b = "tux"; c = false; } `
184221
185- ## 属性集
222+ ### 属性集
186223
187224在 Nix 语法中,属性集(简称集合)是最常见的数据类型之一,基本示例如下:
188225
@@ -203,26 +240,6 @@ foo = {
203240
204241- 集合以 ` { ` ` } ` 为边界,其内部为多个赋值语句,且各个赋值语句末尾必须添加 ` ; ` 。
205242
206- <!-- prettier-ignore -->
207- ::: info 换行与缩进
208- 对于 Nix 语言,大多数情况下,换行与缩进只是为了更好的可读性。
209-
210- 例如,上述代码与下面的代码在本质上并没有区别:
211-
212- ``` nix
213- foo = { a = 1; b = 2; };
214- ```
215-
216- <!-- prettier-ignore -->
217- :::
218-
219- <!-- prettier-ignore -->
220- ::: warning
221- 换行与空格一样具有分隔作用,请勿在不可分隔的地方胡乱断行。
222-
223- <!-- prettier-ignore -->
224- :::
225-
226243上述代码将 ` foo ` 的值定义为集合 ` { a = 1; b = 2; } ` ,因此可称之为集合 ` foo ` 。
227244
228245集合 ` foo ` 中有两个属性:
@@ -258,7 +275,7 @@ foo.b.d = 3;
258275<!-- prettier-ignore -->
259276:::
260277
261- ## 递归属性集
278+ ### 递归属性集
262279
263280普通的属性集不支持递归引用,举个例子:
264281``` nix
@@ -286,7 +303,7 @@ rec {
286303{ a = 1; b = 3; }
287304```
288305
289- 可以看到,结果中的 ` a = 1 ` 在前面,` b = 2 ` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与** 属性名称本身的排序** 有关。例如,对 ` rec { a = 1; b = 2; } ` 与 ` rec { b = 2; a = 1; } ` 的求值,都会把 ` a = 1 ` 放在前面,归因到底,这只是因为 ` a ` 在字母表中位于 ` b ` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。)
306+ 可以看到,结果中的 ` a = 1 ` 在前面,` b = 3 ` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与** 属性名称本身的排序** 有关。例如,对 ` rec { a = 1; b = 2; } ` 与 ` rec { b = 2; a = 1; } ` 的求值,都会把 ` a = 1 ` 放在前面,归因到底,这只是因为 ` a ` 在字母表中位于 ` b ` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。)
290307
291308既然如此,将上面属性集里的两个元素位置对调:
292309``` nix
@@ -295,7 +312,7 @@ rec {
295312 a = 1;
296313}
297314```
298- 你会发现,Nix 也能自动处理求值顺序 ,并不会因为 ` a ` 的声明被调整到后面而影响求值结果(与之前的完全一致,这里就不贴了 )。这看起来相当“智能”,你甚至可以写得更复杂一些,比如 Nix 也能自动处理下面的例子 (结果略):
315+ 你会发现,Nix 解释器能自动处理求值顺序 ,并不会因为 ` a ` 的声明被调整到后面而影响求值结果(与之前的完全一致,从略 )。这看起来相当“智能”,你甚至可以写得更复杂一些,比如 Nix 解释器也能自动处理下面的例子 (结果略):
299316``` nix
300317rec {
301318 c = a * 2 - b + d - 35;
@@ -319,34 +336,41 @@ rec {
319336 b = «error: infinite recursion encountered»;
320337}
321338```
322- 从这个输出来看,我们可以发现递归属性集内部在处理求值顺序的机制 ,确实是递归的,而如果递归陷入死循环就会报错。
339+ 由此可见,递归属性集内部处理求值顺序的机制 ,确实是递归的,而如果递归陷入死循环就会报错。
323340
324- ## ` let ` 绑定
341+ ### 列表
342+ 之前我们学习了属性集,它含有多个元素,例如:
343+ ``` nix
344+ fruits = {
345+ a = "apple";
346+ b = "orange";
347+ c = "banana";
348+ }
349+ ```
350+ 上面的名称 ` a ` ` b ` ` c ` 或许可以有明确的含义,但有时我们不需要这些名称,而只关心后面的值,这种情况下就可以使用列表,例如:
351+ ``` nix
352+ fruits = [ "apple" "orange" "banana" ]
353+ ```
354+ 需要注意语法细节:
355+ - 列表以 ` [ ` ` ] ` 为边界,其内部为多个元素,每个元素都是值(value)而不是赋值语句。
356+ - 元素之间使用空格(或换行)分隔,各元素** 不** 以 ` ; ` 结尾。
325357
326- 一个完整的 ` let ` 绑定有两个部分: ` let ` 绑定名称与值, ` in ` 使用名称。在 ` let `
327- 与 ` in ` 之间的语句中,你可以声明需要被复用的名称,并将其与值绑定。它们可以在
328- ` in ` 之后的表达式中发挥作用:
358+ ## let 绑定与属性访问
359+ 前面关于变量的赋值与使用是非常基本的,我们还需要更灵活的处理方法。本节将会介绍用于定义局部变量的 ** let 绑定** ,以及风格简洁的** 属性访问** 。
360+ ### ` let ` 绑定
361+ 有时我们希望定义一个变量,使其不影响全局,仅在局部生效。此时就可以使用 ` let ` 绑定,示例如下:
329362
330363``` nix
331364let
332- b = a + 1;
333365 a = 1;
366+ b = 2;
334367in
335- a + b
368+ a + b # 结果是 2
336369```
337370
338- 引用到 ` a ` 的地方有两处,它们都会将 ` a ` "替换"成值来计算或赋值,类似于常量。
339-
340- <!-- prettier-ignore -->
341- ::: tip
342- 你不需要关心名称的声明顺序,不会出现名称未定义的情况。
343-
344- <!-- prettier-ignore -->
345- :::
346-
347- ** ` in ` 后面只能跟随一个表达式,并且 ` let ` 绑定的名称只在该表达式是有效的的** ,这
348- 里演示一个列表:
349-
371+ 注意语法细节:
372+ - ` let ` 与 ` in ` 之间的赋值语句以 ` ; ` 结尾;
373+ - ` in ` 之后** 只有一个表达式** 。注意,这只是语法形式上的要求,并不代表 ` let ` 绑定的用处很有限,因为表达式本身可以很复杂,常见的是嵌套属性集。作为基本示例,下面演示刚刚学到的列表:
350374``` nix
351375let
352376 b = a + 1;
@@ -355,17 +379,14 @@ let
355379in
356380 [ a b c ]
357381```
358-
359- 输出的值为:
360-
361- ``` nix
382+ 求值的结果如下:
383+ ```
362384[ 1 2 3 ]
363385```
364386
365387<!-- prettier-ignore -->
366- ::: danger 作用域
367- ** ` let ` 绑定是有作用域的,绑定的名称只能在作用域使用,或者说每个 ` let ` 绑定的名
368- 称只能在该表达式内使用:**
388+ ::: info 作用域
389+ ` let ` 绑定是有作用域的,绑定的名称只能在作用域使用,或者说每个 ` let ` 绑定的名称只能在该表达式内使用。例如下面的例子:
369390
370391``` nix
371392{
374395}
375396```
376397
377- ` x ` 未定义 :
398+ 由于 ` b = x; ` 不在作用域之内,会有报错如下 :
378399
379- ``` bash
400+ ```
380401error: undefined variable 'x'
381-
382- at «string»:3:7:
383-
384- 2| a = let x = 1; in x;
385- 3| b = x;
386- | ^
387- 4| }
388402```
389403
390404<!-- prettier-ignore -->
391405:::
392406
393- ## 属性访问
407+ ### 属性访问
394408
395409使用 ` . ` 访问属性:
396410
419433 a.b.c
420434```
421435
422- ## ` with ` 表达式
436+ ## 语法糖 ` with ` 和 ` inherit `
437+ 语法糖(syntactic sugar)是对语言功能没有影响,但更方便使用的一种语法。本节将介绍两种常用的语法糖 ` with ` 和 ` inherit ` 。
438+ ### ` with ` 表达式
423439
424440` with ` 表达式可以让你少写几次属性集的名称,是个语法糖:
425441
@@ -463,7 +479,7 @@ error: undefined variable 'x'
463479 11| }
464480```
465481
466- ## ` inherit ` 表达式
482+ ### ` inherit ` 表达式
467483
468484` inherit ` 本意就是继承,我们可以使用它完成一对命名相同的名称和属性之间的赋值:
469485
0 commit comments