From 99a07b8b2846a8265b66ea49a6009f0eebdf5172 Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Sat, 8 Jul 2023 17:22:49 +0800 Subject: [PATCH] feat: the nix language --- docs/zh/the-nix-language/index.md | 112 +++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 31 deletions(-) diff --git a/docs/zh/the-nix-language/index.md b/docs/zh/the-nix-language/index.md index f291213..e977cd1 100644 --- a/docs/zh/the-nix-language/index.md +++ b/docs/zh/the-nix-language/index.md @@ -1,6 +1,5 @@ # Nix 语言入门 - Nix 语言是 Nix 包管理器的基础,要想玩得转 NixOS 与 Nix Flakes,享受到它们带来的诸多好处,就必须学会这门语言。 Nix 是一门比较简单的函数式语言,在已有一定编程基础的情况下,过一遍这些语法用时应该在 2 个小时以内,本文假设你具有一定编程基础(也就是说写得不会很细)。 @@ -18,7 +17,7 @@ Nix 是一门比较简单的函数式语言,在已有一定编程基础的情 先把语法过一遍,有个大概的印象就行,后面需要用到时再根据右侧目录回来复习。 -## 1. 基础数据类型一览 {#basic-data-types} +## 基础数据类型一览 {#basic-data-types} 下面通过一个 attribute set (这类似 json 或者其他语言中的 map/dict)来简要说明所有基础数据类型: @@ -52,7 +51,7 @@ Nix 是一门比较简单的函数式语言,在已有一定编程基础的情 bool -> bool ``` -## 2. let ... in ... {#let-in} +## let ... in ... {#let-in} Nix 的 `let ... in ...` 语法被称作「let 表达式」或者「let 绑定」,它用于创建临时使用的局部变量: @@ -65,7 +64,24 @@ a + a # 结果是 2 let 表达式中的变量只能在 `in` 之后的表达式中使用,理解成临时变量就行。 -## 3. attribute set 说明 {#attribute-set} +## if...then...else... {#if-then-else} + +if...then...else... 用于条件判断,它是一个有返回值的表达式,语法如下: + +```nix +if 3 > 4 then "yes" else "no" # 结果为 "no" +``` + +也可以与 let...in... 一起使用: + +```nix +let + x = 3; +in + if x > 4 then "yes" else "no" # 结果为 "no" +``` + +## attribute set 说明 {#attribute-set} 花括号 `{}` 用于创建 attribute set,也就是 key-value 对的集合,类似于 JSON 中的对象。 @@ -123,7 +139,7 @@ a?b # 结果是 true,因为 a.b 这个属性确实存在 has attribute 操作符在 nixpkgs 库中常被用于检测处理 `args?system` 等参数,以 `(args?system)` 或 `(! args?system)` 的形式作为函数参数使用(叹号表示对 bool 值取反,是常见 bool 值运算符)。 -## 4. with 语句 {#with-statement} +## with 语句 {#with-statement} with 语句的语法如下: @@ -144,7 +160,7 @@ in with a; [ x y z ] # 结果是 [ 1 2 3 ], 等价于 [ a.x a.y a.z ] ``` -## 5. 继承 inherit ... {#inherit} +## 继承 inherit ... {#inherit} `inherit` 语句用于从 attribute set 中继承成员,同样是一个简化代码的语法糖,比如: @@ -173,7 +189,7 @@ in } # 结果是 { x = 1; y = 2; } ``` -## 6. ${ ... } 字符串插值 {#string-interpolation} +## ${ ... } 字符串插值 {#string-interpolation} `${ ... }` 用于字符串插值,懂点编程的应该都很容易理解这个,比如: @@ -184,11 +200,11 @@ in "the value of a is ${a}" # 结果是 "the value of a is 1" ``` -## 7. 文件系统路径 {#file-system-path} +## 文件系统路径 {#file-system-path} Nix 中不带引号的字符串会被解析为文件系统路径,路径的语法与 Unix 系统相同。 -## 8. 搜索路径 {#search-path} +## 搜索路径 {#search-path} > 请不要使用这个功能,它会导致不可预期的行为。 @@ -198,7 +214,7 @@ Nix 会在看到 `` 这类三角括号语法时,会在 `NIX_PATH` 环 在这里做个介绍,只是为了让你在看到别人使用类似的语法时不至于抓瞎。 -## 9. 多行字符串 {#multi-line-string} +## 多行字符串 {#multi-line-string} 多行字符串的语法为 `''`,比如: @@ -210,7 +226,7 @@ Nix 会在看到 `` 这类三角括号语法时,会在 `NIX_PATH` 环 '' ``` -## 10. 函数 {#nix-function} +## 函数 {#nix-function} 函数的声明语法为: @@ -297,7 +313,7 @@ pkgs.lib.strings.toUpper "search paths considered harmful" # 结果是 "SEARCH 可以通过 [Nixpkgs Library Functions - Nixpkgs Manual](https://nixos.org/manual/nixpkgs/stable/#sec-functions-library) 查看 lib 函数包的详细内容。 -## 11. 不纯(Impurities) {#impurities} +## 不纯(Impurities) {#impurities} Nix 语言本身是纯函数式的,是纯的,「纯」是指它就跟数学中的函数一样,同样的输入永远得到同样的输出。 @@ -307,7 +323,7 @@ Nix 有两种构建输入,一种是从文件系统路径等输入源中读取 > Nix 中的搜索路径与 `builtins.currentSystem` 也是不纯的,但是这两个功能都不建议使用,所以这里略过了。 -## 12. Fetchers {#fetchers} +## Fetchers {#fetchers} 构建输入除了直接来自文件系统路径之外,还可以通过 Fetchers 来获取,Fetcher 是一种特殊的函数,它的输入是一个 attribute set,输出是 Nix Store 中的一个系统路径。 @@ -328,34 +344,68 @@ builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc6343 # result example(auto unzip the tarball) => "/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source" ``` -## 13. Derivations {#derivations} +## Derivations {#derivations} -一个构建动作的 Nix 语言描述被称做一个 Derivation,它描述了如何构建一个软件包,它的构建结果是一个 Store Object. +Derivation 描述了如何构建一个软件包,是一个软件包构建流程的 Nix 语言描述,它声明了构建时需要有哪些依赖项、需要什么构建工具链、要设置哪些环境变量、哪些构建参数、先干啥后干啥等等。 +Derivation 的构建结果是一个 Store Object,其中包含了软件包的所有二进制程序、配置文件等等内容。 Store Object 的存放路径格式为 `/nix/store/-`,其中 `` 是构建结果的 hash 值,`` 是它的名字。路径 hash 值确保了每个构建结果都是唯一的,因此可以多版本共存,而且不会出现依赖冲突的问题。 -`/nix/store` 被称为 Store,存放所有的 Store Objects,这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。 +`/nix/store` 是一个特殊的文件路径,它被称为 Store,存放所有的 Store Objects,这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。 -在 Nix 语言的最底层,一个构建任务就是使用 builtins 中的不纯函数 `derivation` 创建的,我们实际使用的 `stdenv.mkDerivation` 就是它的一个 wrapper,屏蔽了底层的细节,简化了用法。 +Derivation 实质上只是一个 attribute set,Nix 底层会使用内置函数 `builtins.derivation` 将这个 attribute set 构建为一个 Store Object。 +我们实际编写 Derivation 时,通常使用的是 `stdenv.mkDerivation`,它只前述内置函数 `builtins.derivation` 的 Nix 语言 wrapper,屏蔽了底层的细节,简化了用法。 -## 14. if...then...else... {#if-then-else} - -if...then...else... 用于条件判断,它是一个有返回值的表达式,语法如下: +一个简单的 Derivation 如下,它声明了一个名为 hello 的应用程序(摘抄自 [nixpkgs/pkgs/hello](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/hello/default.nix)): ```nix -if 3 > 4 then "yes" else "no" # 结果为 "no" +{ callPackage +, lib +, stdenv +, fetchurl +, nixos +, testers +, hello +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "hello"; + version = "2.12.1"; + + src = fetchurl { + url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz"; + sha256 = "sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA="; + }; + + doCheck = true; + + passthru.tests = { + version = testers.testVersion { package = hello; }; + + invariant-under-noXlibs = + testers.testEqualDerivation + "hello must not be rebuilt when environment.noXlibs is set." + hello + (nixos { environment.noXlibs = true; }).pkgs.hello; + }; + + passthru.tests.run = callPackage ./test.nix { hello = finalAttrs.finalPackage; }; + + meta = with lib; { + description = "A program that produces a familiar, friendly greeting"; + longDescription = '' + GNU Hello is a program that prints "Hello, world!" when you run it. + It is fully customizable. + ''; + homepage = "https://www.gnu.org/software/hello/manual/"; + changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${finalAttrs.version}"; + license = licenses.gpl3Plus; + maintainers = [ maintainers.eelco ]; + platforms = platforms.all; + }; +}) ``` -也可以与 let...in... 一起使用: - -```nix -let - x = 3; -in - if x > 4 then "yes" else "no" # 结果为 "no" -``` - - ## 参考 - [Nix language basics - nix.dev](https://nix.dev/tutorials/first-steps/nix-language)