UP | HOME

进阶手册

Table of Contents

数据类型

原始数据类型

字符串

单行字符串

与大多数编程语言的字符串一致,使用双引号闭合:

"Hello, nix!\n"
多行字符串

多行字符串是通过 两个单引号 闭合的

''
This is the first line.
This is the second line.
This is the third line.
''

多行字符串往往会带有不同程度的缩进,会被进一步处理。也就是说对于以下字符串:

''
  This is the first line.
  This is the second line.
    This is the third line.
''

会被“智能缩进”处理,每一行都被前移了最小缩进数个字符。处理后的结果是:

This is the first line.
This is the second line.
  This is the third line.

同时,假如第一行被占空了,也会对其进行处理:

''

There's a row of spaces up there.
''

处理后的数据是:


There's a row of spaces up there.

Nix 只会将自动处理后的字符串当作输入,而不是原始字符串 raw string

URL

为了书写简便, RFC 2396 规定了对于 URI 可以 不使用 引号 闭合:

UriWithoutQuotes = http://example.org/foo.tar.bz2
  UriWithQuotes = "http://example.org/foo.tar.bz2"

两者是等价的

数字

数字被分为

  • 浮点型

    比如 .114514
    
  • 整型

    比如 2233
    
数字是类型兼容的:纯整数运算总是返回整数,而任何涉及至少一个浮点数的运算都会返回一个浮点数

路径

路径 至少 需要包含一个斜杠才能被识别为路径:

/foo/bar/bla.nix
  ~/foo/bar.nix
  ../foo/bar/qux.nix

除了某些尖括号路径(比如 <nixpkgs> )外,其他路径都支持 字符串插值

"${./foo.txt}"

布尔

truefalse

字面意义上的 null

列表

列表使用 中括号 闭合, 空格 分隔元素,一个列表允许包含 不同类型 的值:

[ 123 ./foo.nix "abc" (  x: x+1) ]
此处如果不给 f { x = y; } 打上括号,就会把函数也当作此列表的值

属性集

属性集是用 大括号 括起来的 名称与值对 (称为属性)的集合:

  • 属性名:可以是 标识符字符串
    • 标识符必须以字母或下划线开头,可以包含字母、数字、 下划线单引号 '连接符 -
{
  x = 123;
  text = "Hello";
  y = { bla = 456; };
}

使用 . 访问各个属性:

{ a = "Foo"; b = "Bar"; }.a  #"Foo"

使用 or 关键字,可以在属性选择中提供 默认值

{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" # ":Xyzzy" 
因为属性 c 不在属性集里,故输出默认值

也可以用字符串去访问属性:

{ "$!@#?" = 123; }."$!@#?" # 123

属性名也支持字符串插值:

let bar = "foo"; in { foo = 123; }.${bar} # 123

  let bar = "foo"; in { ${bar} = 123; }.foo # 123 
两者的值都是123 

在特殊情况下,如果集合声明中的属性名求值为 null(这是错误的,因为 null 不能被强制为字符串),那么该属性将不会被添加到集合中:

{ ${if foo then "bar" else null} = true; }
如果 foo 的值为 false,则其值为 {} 

如果一个集合的 __functor 属性的值是 可调用的 (即它本身是一个函数或是其中一个集合的 __functor 属性的值是可调用的),那么它就可以 像函数一样被应用 ,首先传入的是集合本身,例如:

let add = { __functor = self: x: x + self.x; }; # 定义 属性集 add 的 __functor 属性 
    inc = add // { x = 1; };  # inc属性集相当于把 { x = 1; } 属性集 传入 add,这样 inc 属性集里的  self.x = 1 
in inc 2 #  3  这里传入的2 对应于 inc 里的x ,相当 x + self.x = 2 + 1 = 3
这可用于为函数附加元数据,而调用者无需对其进行特殊处理

也可用于实现面向对象编程等形式

数据构造

递归属性集

let绑定

继承属性

函数

条件判断

Nix 的条件判断表达式的结构类似于这样:

if <exprCond> then <exprThen> else <exprElse>

其中 exprCond 的求值结果必须为 布尔值 truefalse

  • 当 exprCond 求值为 true 时,上述条件表达式的结果为 exprThen
  • 否则结果为 exprElse

定义时的使用例子如下:

# 利用 if-then-else 表达式实现函数:
myFunction = x: if x > 0 then "Positive" else "Non-positive"
  myFunction 0 # Non-positive
  myFunction 1 # Positive

利用 if-then-else 表达式定义变量:

no = 7
  gt0 = if no > 0 then "yes" else "no"
    # gt0 变量值为 "yes"
    gt0 # => "yes"

亦可嵌套使用

# 利用 if-then-else 表达式实现函数:
myPlan = target: if target == "fitness" then "I'm going swimming."
                                   else if target == "purchase" then "I'm going shopping."
                                   else if target == "learning" then "I'm going to read a book."
                                   else "I'm not going anywhere."
                               myPlan "fitness" # "I'm going swimming."
                               myPlan null # "I'm not going anywhere."

                               #  利用 if-then-else 表达式定义变量:
                               x = null
                                 text =
                                           if x == "a" then
                                             "hello"
                                           else if x == "b" then
                                             "hi"
                                           else if x == "c" then
                                             "ciao"
                                           else
                                             "x is invalid"  # "x is invalid"

循环控制

Nix 是一种函数式编程语言,每一段 Nix 程序都是一个完整的表达式

这与流行的 Java, Python 等命令式编程语言有很大不同,命令式编程语言的程序往往是一段包含变量声明、赋值、跳转等指令的指令序列。

这里不展开说明函数式编程与命令式编程的区别

一言蔽之,Nix 语言中 没有 while/for 循 环这类控制结构,取而代之的是以 递归 为基础实现的一系列 高阶函数

Nix 的标准库提供了一系列此类高阶函数用于对字符串、列表等可迭代对象进行操作

内建函数与 nixpkgs.lib 函数库

Nix 语言自身提供了一些精简的内建函数,可通过 builtins.xxx 这种方式使用

官方文档参见 https://nix.dev/manual/nix/2.22/language/builtins#built-in-functions 

此外,官方函数库 nixpkgs.lib 提供了比 builtins 更丰富的功能

社区提供的 https://noogle.dev/ 可以相当容易地搜索上述两类函数,推荐一用

下面针对 builtins 与 nixpkgs.lib 的用法各举一例:

# 通过 nixpkgs.lib.range,生成指定范围元素的列表
nixpkgs = import <nixpkgs> {}
  alist = nixpkgs.lib.range 4 7
    alist # => [ 4 5 6 7 ]

    # 通过内建函数 filter,遍历列表中的元素并过滤
    builtins.filter (item: item == "hello") [ "hello" "world" ] # => [ "hello" ]

递归函数

如前所述,Nix 语言作为一种函数式编程语言,它采用递归取代了命令式编程语言中的循环等控制结构

这里举个简单的例子来说明 Nix 语言中如何实现一个递归函数:

let
  recurse = n: if n <= 0 then [] else recurse (n - 1) ++ [n];
in
recurse 5 # [1 2 3 4 5] 
️注意:对新手而言,nixpkgs.lib 中的函数基本够用了

建议优先查找使用内建函数和 nixpkgs.lib 中的函数,如果找不到自己想要的功能再考虑自行实现

断言

with表达式