Skip to content

Commit 713c9b7

Browse files
authored
大修Nix语言快速入门(第三部分) (#49)
* 大修Nix语言快速入门(第三部分) - 在正文开头添加关于注释的说明 - 添加关于集合的说明 - 修改 let 绑定一节 - 将缩进与换行移动到注释部分 - 调整章节结构 - 一些小修正 * 复原 let 绑定一节中关于列表的例子 在若干小节开头添加导入语 * 给代码块补上nix * 添加面向维护者的说明 * 更新维护者说明部分
1 parent b7f2130 commit 713c9b7

File tree

1 file changed

+82
-66
lines changed

1 file changed

+82
-66
lines changed

src/tutorials/lang/QuickOverview.md

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<!-- prettier-ignore -->
1111
:::
1212

13+
<details><summary>仅面向本文维护者的说明,单击以切换折叠/展开</summary>
14+
本文在设计上是线性的,也即只需要读者具备一点点基础,就可以通过按顺序从头读到尾的方式完成本文的学习。因此,请留心说明顺序,例如讲 let 绑定时如果举了一个列表的例子,你需要确保前面已经正式介绍过列表。再如,讲 with 语法糖的时候同时用到 let 绑定和列表,那么这两个概念都需要在前面已经正式介绍过。否则,读者很可能会面对初次接触的语法或者概念而被卡住,这会严重影响学习效率甚至是完成率。若出于顺序安排的其他合理性原因,实在无法避开在说明中涉及陌生概念,可以提示读者相关部分不需要理解,后面会讲到。
15+
</details>
16+
1317
Nix 作为语言,是一门简单的函数式语言,它被专门设计并用于 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
300317
rec {
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
331364
let
332-
b = a + 1;
333365
a = 1;
366+
b = 2;
334367
in
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
351375
let
352376
b = a + 1;
@@ -355,17 +379,14 @@ let
355379
in
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
{
@@ -374,23 +395,16 @@ in
374395
}
375396
```
376397

377-
`x` 未定义
398+
由于 `b = x;` 不在作用域之内,会有报错如下
378399

379-
```bash
400+
```
380401
error: 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

@@ -419,7 +433,9 @@ in
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

Comments
 (0)