44::: warning 基础要求
55以下教程需要你具有一定基础。
66具体来说,如果你已经知道在编程领域什么是变量(variable)、
7- 赋值(assign)、 字符串(string)、函数(function)及参数(argument),
7+ 字符串(string)、函数(function)及参数(argument),
88那么你的知识水平就差不多足够了。
99
1010<!-- prettier-ignore -->
@@ -27,7 +27,7 @@ Nix 作为语言,是一门简单的函数式语言,它被专门设计并用
2727::: warning
2828本节需要你已经安装了 Nix 或正在使用 NixOS。
2929
30- 另外,本教程中的示例代码一般不是为了供直接运行而写的 。对于每一段代码,若想实践其
30+ 另外,本教程中的示例代码并不全是为了供直接运行而写的 。对于每一段代码,若想实践其
3131效果,请先理解对应的知识,再基于这段代码自己编写测试代码以运行。
3232
3333(对于 Nix 来说,运行代码被称为** 求值** (evaluate),而只有** 表达
@@ -59,7 +59,7 @@ nix-repl>
59591 + 2
6060```
6161
62- 回车,得到输出结果如下 :
62+ 回车即可求值,得到结果如下 :
6363
6464``` plain
65653
@@ -77,7 +77,7 @@ nix-repl>
7777交互模式简单快捷,但我们平时使用 Nix 语言进行编辑配置、打包等操作时,大多数情况
7878下不会直接使用交互模式,而是对 ` *.nix ` 纯文本文件进行编辑。
7979
80- 因此,如果你习惯于使用编辑器,这里更推荐利用文件求值进行实践。
80+ 因此,如果你习惯于使用编辑器,这里更推荐利用文件求值进行实践。每个 nix 文件的内容都是 ** 一个 ** 表达式,这是 nix 文件能被求值的前提。
8181
8282例如,新建文件 ` foo.nix ` ,将其内容编辑如下:
8383
@@ -105,7 +105,7 @@ nix-instantiate --eval foo.nix
105105<details ><summary >单击以切换折叠/展开</summary >
106106
107107Nix 的求值具有惰性(laziness),只会在有必要时进行。例如,下述代码(看不懂没关
108- 系)将 ` a ` 的值赋为 $\dfrac{2}{0}$ ,这是一种典型的数学错误:
108+ 系)将名称 ` a ` 分配给值 $\dfrac{2}{0}$ ,这是一种典型的数学错误:
109109
110110``` nix
111111let a = builtins.div 2 0; b = 3; in b
@@ -116,7 +116,7 @@ let a = builtins.div 2 0; b = 3; in b
116116
117117---
118118
119- 与惰性类似的是另一种行为是,嵌套集合的求值 ,在交互模式和文件求值模式下,除非必
119+ 与惰性类似的是另一种行为是,嵌套属性集的求值 ,在交互模式和文件求值模式下,除非必
120120要,默认不会迭代,而是以占位符替代,例如
121121
122122``` nix
@@ -165,16 +165,16 @@ let a = builtins.div 2 0; b = 3; in b
165165- 注释:在 Nix 语言中,用 ` # ` 表示注释,在它之后直到行末的部分都会被忽略。
166166- 缩进与换行:与 Python 这种对缩进有要求的语言不同,在 Nix 语言中,大多数情况下,换行与缩进只是为了更好的可读性,并不影响代码的本质。
167167
168- 例如,下面有两段示例代码 (你目前还不需要理解它们的含义),它们在本质上(也即在 Nix 解释器看来)并没有区别。
168+ 例如,下面的两段示例代码 (你目前还不需要理解它们的含义),它们在本质上(也即在 Nix 解释器看来)并没有区别。
169169
170170第一例:
171171``` nix
172- foo = { a = 1; b = 2; }
172+ { a = 1; b = 2; }
173173```
174174
175175第二例:
176176``` nix
177- foo = { # 这是一句注释,放在代码末尾。
177+ { # 这是一句注释,放在代码末尾。
178178 # 这也是一句注释,单独占了一行。
179179 a = 1; # 这里即使不缩进,也不影响代码本质。
180180 b = 2;
@@ -189,21 +189,29 @@ foo = { # 这是一句注释,放在代码末尾。
189189<!-- prettier-ignore -->
190190:::
191191
192- ## 赋值与集合
193- 与大多数编程语言类似,变量与值是 Nix 语言中最基础的概念。本节将会介绍 Nix 中如何为变量** 赋值** ,以及最常用的数据类型——** 属性集** ,继而引出** 递归属性集** 与** 列表** 的概念。
192+ ## 名称与属性集
193+ 变量是大多数编程语言中最基础的概念,而与之类似的名称则是 Nix 语言中最基础的概念。本节将会介绍 Nix 中如何将名称** 分配给值** ,以及最常用的数据类型——** 属性集** ,继而引出** 递归属性集** 与** 列表** 的概念。
194+
194195### 名称和值
195196
196- 我们可以使用 ` = ` 为名称绑定值,形成赋值语句 。例如将名称 ` foo ` 赋值为 ` 123 ` :
197+ 我们可以使用 ` = ` 将名称分配给值,形成“名称 - 值”对 。例如将名称 ` foo ` 分配给值 ` 123 ` :
197198
198199``` nix
199200foo = 123
200201```
201202
202203<!-- prettier-ignore -->
203- ::: info 名称与其它语言中变量的区别
204- 在很多语言中,赋值会改变变量的状态,而 Nix 语言中的名称一旦赋值(定义)就无法改变。
204+ ::: info 函数式语言与命令式语言中“变量”的区别
205+ | 维度 | 命令式语言 | Nix(函数式) |
206+ | ---| ---| ---|
207+ | 底层模型 | 存储格(内存单元) | 无存储格,只有「名称-值」映射 |
208+ | 操作 | 赋值:随时把新值写回同一单元 | 绑定:一次性把名称贴到值,不可重写 |
209+ | 所谓“变量” | 存储格的别名 → 之后可反复擦写 | 数学意义上的变量 → 同一作用域内值固定 |
210+ | 文档用词 | variable = 可重写的存储格 | variable = 一次性绑定的名称(不会变) |
211+
212+ 一句话:
213+ 在 Nix 里,“名称”就是“变量”,只是这个变量一旦绑定便成永恒;它保留了数学“可取不同值”的语义,却丢掉了命令式“可重新赋值”的存储格含义。
205214
206- 不过,将 Nix 中的名称称为变量也没问题。
207215
208216<!-- prettier-ignore -->
209217:::
@@ -221,7 +229,7 @@ foo = 123
221229
222230### 属性集
223231
224- 在 Nix 语法中,属性集(简称集合)是最常见的数据类型之一 ,基本示例如下:
232+ 在 Nix 语法中,属性集是最常见的数据类型之一 ,基本示例如下:
225233
226234``` nix
227235foo = {
@@ -233,22 +241,22 @@ foo = {
233241概念说明:
234242
235243- 属性集(attribute set)就是装载若干对** 名称与值** 的集合。
236- - 集合内的名称被称为这个集合的 ** 属性** (attribute);
237- - 集合内由名称和值组成的对被称为该属性的 ** 元素** (element);
244+ - 属性集内的名称被称为这个属性集的 ** 属性** (attribute);
245+ - 属性集内由名称和值组成的对被称为该属性的 ** 元素** (element);
238246
239247语法说明:
240248
241- - 集合以 ` { ` ` } ` 为边界,其内部为多个赋值语句,且各个赋值语句末尾必须添加 ` ; ` 。
249+ - 属性集以 ` { ` ` } ` 为边界,其内部为多个“名称-值”对,且它们末尾必须添加 ` ; ` 。
242250
243- 上述代码将 ` foo ` 的值定义为集合 ` { a = 1; b = 2; } ` ,因此可称之为集合 ` foo ` 。
251+ 上述代码将 ` foo ` 的值定义为属性集 ` { a = 1; b = 2; } ` ,因此可称之为属性集 ` foo ` 。
244252
245- 集合 ` foo ` 中有两个属性:
253+ 属性集 ` foo ` 中有两个属性:
246254
247255- 属性 ` a ` ,其值为 ` 1 `
248256- 属性 ` b ` ,其值为 ` 2 `
249257
250- 属性的值除了可以是 ` 1 ` ` 2 ` 这样的数值外,也可以是一个集合 (也即支持嵌套),例如
251- 将 ` b ` 的值改为集合 ` { c = 2; d = 3; } ` :
258+ 属性的值除了可以是 ` 1 ` ` 2 ` 这样的数值外,也可以是一个属性集 (也即支持嵌套),例如
259+ 将 ` b ` 的值改为属性集 ` { c = 2; d = 3; } ` :
252260
253261``` nix
254262foo = {
@@ -260,7 +268,7 @@ foo = {
260268};
261269```
262270
263- 嵌套集合中的属性也可以利用 ` . ` 表示,例如上面这段的一种等价写法如下:
271+ 嵌套属性集中的属性也可以利用 ` . ` 表示,例如上面这段的一种等价写法如下:
264272
265273``` nix
266274foo.a = 1;
@@ -288,7 +296,7 @@ foo.b.d = 3;
288296```
289297error: undefined variable 'a'
290298```
291- 可见,当属性集内的属性 ` b ` 需要访问该集合的另一个属性 ` a ` 时,即使 ` a ` 是“先”定义的,也无法访问到。此时就需要我们改用递归(recursive)属性集,它相比普通的属性集,在前面多加了 ` rec ` :
299+ 可见,当属性集内的属性 ` b ` 需要访问该属性集的另一个属性 ` a ` 时,即使 ` a ` 是“先”定义的,也无法访问到。此时就需要我们改用递归(recursive)属性集,它相比普通的属性集,在前面多加了 ` rec ` :
292300
293301``` nix
294302rec {
@@ -303,7 +311,7 @@ rec {
303311{ a = 1; b = 3; }
304312```
305313
306- 可以看到,结果中的 ` a = 1 ` 在前面,` b = 3 ` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与** 属性名称本身的排序** 有关。例如,对 ` rec { a = 1; b = 2; } ` 与 ` rec { b = 2; a = 1; } ` 的求值,都会把 ` a = 1 ` 放在前面,归因到底,这只是因为 ` a ` 在字母表中位于 ` b ` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。)
314+ 可以看到,结果中的 ` a = 1 ` 在前面,` b = 3 ` 在后面。这种顺序实际上与任何其它因素(包括声明顺序、求值依赖关系)都无关,而只与** 属性名称本身的排序** 有关。例如,对 ` rec { a = 1; b = 2; } ` 与 ` rec { b = 2; a = 1; } ` 的求值,都会把 ` a = 1 ` 放在前面,归因到底,只是 ` a ` 在字母表中位于 ` b ` 之前罢了。(直接原因则与 Nix 解释器对名称排序所用到的算法或者调用的库有关,这里不再深入。)
307315
308316既然如此,将上面属性集里的两个元素位置对调:
309317``` nix
@@ -352,13 +360,13 @@ fruits = {
352360fruits = [ "apple" "orange" "banana" ]
353361```
354362需要注意语法细节:
355- - 列表以 ` [ ` ` ] ` 为边界,其内部为多个元素,每个元素都是值(value)而不是赋值语句 。
363+ - 列表以 ` [ ` ` ] ` 为边界,其内部为多个元素,每个元素都是值(value)。
356364- 元素之间使用空格(或换行)分隔,各元素** 不** 以 ` ; ` 结尾。
357365
358366## let 绑定与属性访问
359- 前面关于变量的赋值与使用是非常基本的 ,我们还需要更灵活的处理方法。本节将会介绍用于定义局部变量的 ** let 绑定** ,以及风格简洁的** 属性访问** 。
367+ 前面关于名称的使用是非常基本的 ,我们还需要更灵活的处理方法。本节将会介绍另一种将名称分配给值的方法—— ** let 绑定** ,以及风格简洁的** 属性访问** 。
360368### ` let ` 绑定
361- 有时我们希望定义一个变量,使其不影响全局,仅在局部生效。 此时就可以使用 ` let ` 绑定,示例如下:
369+ 有时我们希望在指定的范围内为值分配名称, 此时就可以使用 ` let ` 绑定,示例如下:
362370
363371``` nix
364372let
369377```
370378
371379注意语法细节:
372- - ` let ` 与 ` in ` 之间的赋值语句以 ` ; ` 结尾;
380+ - ` let ` 与 ` in ` 之间的“名称-值”对以 ` ; ` 结尾;
373381- ` in ` 之后** 只有一个表达式** 。注意,这只是语法形式上的要求,并不代表 ` let ` 绑定的用处很有限,因为表达式本身可以很复杂,常见的是嵌套属性集。作为基本示例,下面演示刚刚学到的列表:
374382``` nix
375383let
@@ -404,6 +412,18 @@ error: undefined variable 'x'
404412<!-- prettier-ignore -->
405413:::
406414
415+ <!-- prettier-ignore -->
416+ ::: note 局部变量(?)
417+ Nix 中不存在“全局变量”,因而“局部变量”的说法可能引起误会,应当尽量避免使用。
418+
419+ 不过,[ Nix manual] ( https://nix.dev/manual/nix/2.31/language/syntax.html ) 中对 let 绑定的介绍提到了局部变量(local variable)。
420+ > A let-expression allows you to define local variables for an expression.
421+
422+ 这种说法可能不合适,但既然官方文档也有用到,其他地方自然也可能会出现,留心即可。
423+
424+ <!-- prettier-ignore -->
425+ :::
426+
407427### 属性访问
408428
409429使用 ` . ` 访问属性:
424444 attrset.a.b.c
425445```
426446
427- 当然,就像如何访问属性一样,也可以用 ` . ` 直接赋值它 :
447+ 当然,就像如何访问属性一样,也可以用 ` . ` 为值分配名称 :
428448
429449``` nix
430450let
437457语法糖(syntactic sugar)是对语言功能没有影响,但更方便使用的一种语法。本节将介绍两种常用的语法糖 ` with ` 和 ` inherit ` 。
438458### ` with ` 表达式
439459
440- ` with ` 表达式可以让你少写几次属性集的名称,是个语法糖 :
460+ ` with ` 表达式可以让你少写几次属性集的名称:
441461
442462``` nix
443463let
@@ -481,7 +501,7 @@ error: undefined variable 'x'
481501
482502### ` inherit ` 表达式
483503
484- ` inherit ` 本意就是继承,我们可以使用它完成一对命名相同的名称和属性之间的赋值 :
504+ ` inherit ` 本意就是继承,我们可以使用它让属性继承名称的值 :
485505
486506``` nix
487507let
@@ -740,7 +760,7 @@ x: y: x + y
740760{ a, b }: a + b
741761```
742762
743- 为函数指定默认参数,在缺省该参数赋值的情况下 ,它就是默认值:
763+ 为函数指定默认参数,在缺少该参数的值的情况下 ,它就是默认值:
744764
745765``` nix
746766{ a, b ? 0 }: a + b
836856 let g = f 1; in g 2
837857```
838858
839- 也可以一次性赋值 :
859+ 也可以一次性接收两个参数 :
840860
841861``` nix
842862let
895915 f { a = 1; }
896916```
897917
898- 赋值是可选的 ,根据你的需要来:
918+ 传入参数值是可选的 ,根据你的需要来:
899919
900920``` nix
901921let
925945## 命名参数集
926946
927947又名 "` @ ` 模式"。在上文中,我们已经可以接收到额外的参数了,假如我们需要使用某
928- 个额外参数,我们可以使用命名属性集将其接收到一个另外的集合 :
948+ 个额外参数,我们可以使用命名属性集将其接收到一个另外的属性集 :
929949
930950``` nix
931951{a, b, ...}@args: a + b + args.c # 这样声明函数
0 commit comments