learnxinyminutes-docs/zh-cn/nix.md
2024-12-28 04:06:33 -08:00

360 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: Nix
filename: learnnix.nix
contributors:
- ["Chris Martin", "http://chris-martin.org/"]
- ["Rommel Martinez", "https://ebzzry.io"]
- ["Javier Candeira", "https://candeira.com/"]
translators:
- ["Smile", "https://wiki.dev-hub.top"]
---
Nix 是一个为 [Nix 包管理器](https://nixos.org/nix/) 和 [NixOS](https://nixos.org/) 开发的简单函数式语言。
你可以使用 [nix-instantiate](https://nixos.org/nix/manual/#sec-nix-instantiate) 或 [`nix repl`](https://nixos.org/nix/manual/#ssec-relnotes-2.0) 来评估 Nix 表达式。
```nix
with builtins; [
# 注释
#=========================================
# 行内注释看起来像这样。
/* 多行注释
看起来像这样。 */
# 布尔值
#=========================================
(true && false) # 与
#=> false
(true || false) # 或
#=> true
(if 3 < 4 then "a" else "b") # 条件
#=> "a"
# 整数和浮点数
#=========================================
# 有两种数值类型:整数和浮点数
1 0 42 (-3) # 一些整数
123.43 .27e13 # 一对浮点数
# 运算将保留数值类型
(4 + 6 + 12 - 2) # 加法
#=> 20
(4 - 2.5)
#=> 1.5
(7 / 2) # 除法
#=> 3
(7 / 2.0)
#=> 3.5
# 字符串
#=========================================
"字符串字面量用双引号括起来。"
"
字符串字面量可以跨越
多行。
"
''
这被称为“缩进字符串”字面量。
它智能地去除前导空白。
''
''
a
b
''
#=> "a\n b"
("ab" + "cd") # 字符串连接
#=> "abcd"
# 反引号允许你在字符串中嵌入值。
("Your home directory is ${getEnv "HOME"}")
#=> "Your home directory is /home/alice"
# 路径
#=========================================
# Nix 有一个用于路径的原始数据类型。
/tmp/tutorials/learn.nix
# 相对路径在解析时相对于它出现的文件解析为绝对路径。
tutorials/learn.nix
#=> /the-base-path/tutorials/learn.nix
# 路径必须至少包含一个斜杠,因此同一目录中的文件的相对路径需要一个 ./ 前缀,
./learn.nix
#=> /the-base-path/learn.nix
# 如果你希望 / 运算符表示除法,它必须被空格包围。
7/2 # 这是一个路径字面量
(7 / 2) # 这是整数除法
# 导入
#=========================================
# 一个 nix 文件包含一个没有自由变量的顶级表达式。一个导入表达式评估为它导入的文件的值。
(import /tmp/foo.nix)
# 导入也可以通过字符串指定。
(import "/tmp/foo.nix")
# 导入路径必须是绝对路径。路径字面量会自动解析,因此这没问题。
(import ./foo.nix)
# 但字符串不会这样。
(import "./foo.nix")
#=> 错误字符串foo.nix不代表绝对路径
# Let
#=========================================
# `let` 块允许我们将值绑定到变量。
(let x = "a"; in
x + x + x)
#=> "aaa"
# 绑定可以相互引用,并且它们的顺序无关紧要。
(let y = x + "b";
x = "a"; in
y + "c")
#=> "abc"
# 内部绑定会覆盖外部绑定。
(let a = 1; in
let a = 2; in
a)
#=> 2
# 函数
#=========================================
(n: n + 1) # 添加 1 的函数
((n: n + 1) 5) # 同一个函数,应用于 5
#=> 6
# 没有命名函数的语法,但它们可以像任何其他值一样通过 `let` 块绑定。
(let succ = (n: n + 1); in succ 5)
#=> 6
# 一个函数恰好有一个参数。
# 多个参数可以通过柯里化实现。
((x: y: x + "-" + y) "a" "b")
#=> "a-b"
# 我们还可以有命名函数参数,
# 我们将在引入集合后再讨论。
# 列表
#=========================================
# 列表用方括号表示。
(length [1 2 3 "x"])
#=> 4
([1 2 3] ++ [4 5])
#=> [1 2 3 4 5]
(concatLists [[1 2] [3 4] [5]])
#=> [1 2 3 4 5]
(head [1 2 3])
#=> 1
(tail [1 2 3])
#=> [2 3]
(elemAt ["a" "b" "c" "d"] 2)
#=> "c"
(elem 2 [1 2 3])
#=> true
(elem 5 [1 2 3])
#=> false
(filter (n: n < 3) [1 2 3 4])
#=> [ 1 2 ]
# 集合
#=========================================
# “集合”是一个无序的字符串键映射。
{ foo = [1 2]; bar = "x"; }
# . 运算符从集合中提取一个值。
{ a = 1; b = 2; }.a
#=> 1
# ? 运算符测试一个键是否存在于集合中。
({ a = 1; b = 2; } ? a)
#=> true
({ a = 1; b = 2; } ? c)
#=> false
# // 运算符合并两个集合。
({ a = 1; } // { b = 2; })
#=> { a = 1; b = 2; }
# 右边的值覆盖左边的值。
({ a = 1; b = 2; } // { a = 3; c = 4; })
#=> { a = 3; b = 2; c = 4; }
# rec 关键字表示一个“递归集合”,
# 其中属性可以相互引用。
(let a = 1; in { a = 2; b = a; }.b)
#=> 1
(let a = 1; in rec { a = 2; b = a; }.b)
#=> 2
# 嵌套集合可以分段定义。
{
a.b = 1;
a.c.d = 2;
a.c.e = 3;
}.a.c
#=> { d = 2; e = 3; }
# 集合是不可变的,因此你不能重新定义一个属性:
{
a = { b = 1; };
a.b = 2;
}
#=> 属性 'a.b' 在 (string):3:5 处已在 (string):2:11 处定义
# 然而,即使属性本身已被直接赋值,属性的集合成员也可以分段定义。
{
a = { b = 1; };
a.c = 2;
}
#=> { a = { b = 1; c = 2; }; }
# With
#=========================================
# `with` 块的主体在集合的映射绑定到变量时进行评估。
(with { a = 1; b = 2; };
a + b)
# => 3
# 内部绑定会覆盖外部绑定。
(with { a = 1; b = 2; };
(with { a = 5; };
a + b))
#=> 7
# 本教程的第一行以 "with builtins;" 开始,
# 因为 builtins 是一个包含所有内置函数length、head、tail、filter 等)的集合。
# 这使我们不必写 "builtins.length" 而只需写 "length"。
# 集合模式
#=========================================
# 当我们需要将多个值传递给函数时,集合很有用。
(args: args.x + "-" + args.y) { x = "a"; y = "b"; }
#=> "a-b"
# 这可以用集合模式更清晰地写出来。
({x, y}: x + "-" + y) { x = "a"; y = "b"; }
#=> "a-b"
# 默认情况下,模式在包含额外键的集合上失败。
({x, y}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
#=> error: anonymous function called with unexpected argument z
# 添加 ", ..." 允许忽略额外的键。
({x, y, ...}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
#=> "a-b"
# 整个集合可以使用 `@` 绑定到一个变量
(args@{x, y}: args.x + "-" + args.y) { x = "a"; y = "b"; }
#=> "a-b"
# 错误
#=========================================
# `throw` 导致评估中止并显示错误消息。
(2 + (throw "foo"))
#=> error: foo
# `tryEval` 捕获抛出的错误。
(tryEval 42)
#=> { success = true; value = 42; }
(tryEval (2 + (throw "foo")))
#=> { success = false; value = false; }
# `abort` 类似于 throw但它是致命的它无法被捕获。
(tryEval (abort "foo"))
#=> error: evaluation aborted with the following error message: foo
# `assert` 如果为真,则评估为给定值;
# 否则它会抛出一个可捕获的异常。
(assert 1 < 2; 42)
#=> 42
(assert 1 > 2; 42)
#=> error: assertion failed at (string):1:1
(tryEval (assert 1 > 2; 42))
#=> { success = false; value = false; }
# 不纯性
#=========================================
# 因为构建的可重复性对 Nix 包管理器至关重要,
# 函数式纯度在用于描述 Nix 包的 Nix 语言中得到了强调。
# 但也有一些不纯之处。
# 您可以引用环境变量。
(getEnv "HOME")
#=> "/home/alice"
# trace函数用于调试。它将第一个参数打印到stderr并计算第二个参数。
(trace 1 2)
#=> trace: 1
#=> 2
# 您可以将文件写入Nix存储。尽管不纯但这相当安全因为文件名是从其内容的哈希中派生的。您可以从任何地方读取文件。在此示例中我们将文件写入存储然后再读出。
(let filename = toFile "foo.txt" "hello!"; in
[filename (readFile filename)])
#=> [ "/nix/store/ayh05aay2anx135prqp0cy34h891247x-foo.txt" "hello!" ]
# 我们还可以将文件下载到Nix存储中。
(fetchurl "https://example.com/package-1.2.3.tgz")
#=> "/nix/store/2drvlh8r57f19s9il42zg89rdr33m2rm-package-1.2.3.tgz"
]
```
### 进一步阅读
* [Nix Manual - Nix表达式语言](https://nixos.org/nix/manual/#ch-expression-language)
* [James Fisher - Nix 示例 - Part 1: The Nix expression language](https://medium.com/@MrJamesFisher/nix-by-example-a0063a1a4c55)
* [Susan Potter - Nix Cookbook - Nix 示例](https://ops.functionalalgebra.com/nix-by-example/)
* [Zero to Nix - Nix教程](https://zero-to-nix.com/)
* [Rommel Martinez - Nix 家族简介](https://web.archive.org/web/20210121042658/https://ebzzry.io/en/nix/#nix)