
<!-- path: language/index.md -->
# MoonBit（月兔）编程语言

MoonBit 是一个面向云与边缘计算的 AI 原生编程语言工具链。它支持 `wasm`、`wasm-gc`、`js` 与 `native` 目标，并且很适合在一个模块中开发混合后端项目。

**状态**

MoonBit 目前处于 beta-preview 阶段。

MoonBit 已经可以用于生产环境；任何不兼容的更改都会被严格评估，编译器错误也应当是罕见的。MoonBit 由一支在语言工具链方面经验深厚的全职团队开发，因此整个生态发展得很快。

**主要优势**

- 生成比现有解决方案明显更小的 Wasm 文件。
- 更高的运行时性能。
- 先进的编译时性能。
- 简单且实用的数据导向语言设计。

<!-- path: language/introduction.md -->
## 介绍

一个 MoonBit 程序由顶层定义组成，包括：

- 类型定义
- 函数定义
- 常数定义和变量绑定
- `init` 函数，`main` 函数和/或 `test` 块。

### 表达式和语句

MoonBit 区分语句和表达式。在函数体中，只有最后一个子句应该是一个表达式，它作为返回值。例如：

```moonbit
fn foo() -> Int {
  let x = 1
  x + 1
}

fn bar() -> Int {
  let x = 1
  //! x + 1
  x + 2
}
```

表达式包括：

- 值字面量（例如布尔值、数字、字符、字符串、数组、元组、结构体）
- 算术、逻辑或比较操作
- 访问数组元素（例如 `a[0]`），结构体字段（例如 `r.x`），元组组件（例如 `t.0`）等
- 变量和枚举构造器
- 匿名本地函数定义
- `match`，`if`，`loop` 表达式等。

语句包括：

- 命名本地函数定义
- 本地变量绑定
- 赋值
- `return` 语句
- 任何返回类型为 `Unit` 的表达式（例如 `ignore`）

代码块可以包含多个语句和一个表达式，表达式的值是代码块的值。

### 变量绑定

变量可以使用 `let mut` 或 `let` 声明为可变或不可变。可变变量可以重新赋值为新值，而不可变变量则不能。

常量只能在顶层声明，不能更改。

```moonbit
let zero = 0

const ZERO = 0

fn main {
  //! const ZERO = 0 
  let mut i = 10
  i = 20
  println(i + zero + ZERO)
}
```

##### NOTE
顶层变量绑定

- 需要 **显式** 类型注释（除非使用字符串、字节或数字等字面量定义）
- 不能是可变的（使用 `Ref` 代替）

### 命名约定

变量、函数应以小写字母 `a-z` 开头，可以包含字母、数字、下划线和其他非 ASCII 的 Unicode 字符。建议使用 snake_case 命名。

常量、类型应以大写字母 `A-Z` 开头，可以包含字母、数字、下划线和其他非 ASCII 的 Unicode 字符。建议使用 PascalCase 或 SCREAMING_SNAKE_CASE 命名。

#### 关键字

以下是关键字，不应使用：

```json
[
  "as", "else", "extern", "fn", "fnalias", "if", "let", "const", "match", "using",
  "mut", "type", "typealias", "struct", "enum", "extenum", "trait",
  "traitalias", "derive", "while", "break", "continue", "import", "return",
  "throw", "raise", "try", "catch", "pub", "priv", "proof_assert", "proof_let",
  "readonly", "true", "false", "_", "test", "loop", "for", "in", "impl", "with",
  "guard", "async", "is", "suberror", "and", "letrec", "enumview", "noraise",
  "defer", "lexmatch", "where", "declare", "nobreak",
]
```

#### 保留关键字

以下是保留关键字。使用它们会引入警告。它们可能在将来变成关键字。

```json
[
  "module", "move", "ref", "static", "super", "unsafe", "use", "await",
  "dyn", "abstract", "do", "final", "macro", "override", "typeof", "virtual", "yield",
  "local", "method", "alias", "assert", "package", "recur", "using", "enumview",
  "isnot", "define", "downcast", "inherit", "member", "namespace", "static", "upcast",
  "use", "void", "lazy", "include", "mixin", "protected", "sealed", "constructor",
  "atomic", "volatile", "anyframe", "anytype", "asm", "await", "comptime", "errdefer",
  "export", "opaque", "orelse", "resume", "threadlocal", "unreachable", "dynclass",
  "dynobj", "dynrec", "var", "finally", "noasync", "assume",
]
```

### 程序入口

#### `init` 和 `main`

有一个专门的函数称为 `init` 函数。`init` 函数是特殊的：

1. 它没有参数列表也没有返回类型。
2. 同一个包中可以有多个 `init` 函数。
3. `init` 函数不能被显式调用或被其他函数引用。相反，所有 `init` 函数将在初始化包时隐式调用。因此，`init` 函数应该只包含语句。

```moonbit
fn init {
  let x = 1
  println(x)
}
```

还有另一个专门的函数称为 `main` 函数。`main` 函数是程序的主入口，它将在初始化阶段之后执行。

与 `init` 函数相同，它没有参数列表也没有返回类型。

```moonbit
fn main {
  let x = 2
  println(x)
}
```

前两个代码片段将在运行时打印以下内容：

```bash
1
2
```

只有 `main` 包才能定义这样的 `main` 函数。查看 [构建系统教程](../toolchain/moon/tutorial.md) 了解详情。在当前项目中，这通过 `moon.pkg` 配置：

```text
options(
  "is-main": true,
)
```

#### `test`

还有一个称为 `test` 块的顶级结构。`test` 块定义内联测试，例如：

```moonbit
test "test_name" {
  assert_eq(1 + 1, 2)
  assert_eq(2 + 2, 4)
  debug_inspect([1, 2, 3], content="[1, 2, 3]")
}
```

以下内容将使用 `test` 块和 `main` 函数来演示执行结果。我们假设所有 `test` 块都通过测试，除非另有说明。

<!-- path: language/fundamentals.md -->
## 基础

### 内置数据结构

#### Unit

`Unit` 是 MoonBit 中的一个内置类型，它表示没有有意义的值。它只有一个值，写作 `()`。`Unit` 类似于 C/C++/Java 中的 `void`，但与 `void` 不同的是，它是一个真正的类型，可以在任何需要类型的地方使用。

`Unit` 类型通常用作执行某些操作但不产生有意义结果的函数的返回类型：

```moonbit
fn print_hello() -> Unit {
  println("Hello, world!")
}
```

与其他一些语言不同，MoonBit 将 `Unit` 视为一等类型，允许它在泛型中使用、存储在数据结构中以及作为函数参数传递。

#### 布尔值

MoonBit 有一个内置的布尔类型，它有两个值：`true` 和 `false`。布尔类型用于条件表达式和控制结构。可以使用 `!` 对布尔值取反；`not(x)` 等价。

```moonbit
let a = true
let b = false
let c = a && b
let d = a || b
let e = !a
let f = !(a && b)
```

#### 数字

MoonBit 有整数类型和浮点类型：

| 类型       | 描述                   | 示例                         |
|----------|----------------------|----------------------------|
| `Int16`  | 16 位有符号整数            | `(42 : Int16)`             |
| `Int`    | 32 位有符号整数            | `42`                       |
| `Int64`  | 64 位有符号整数            | `1000L`                    |
| `UInt16` | 16 位无符号整数            | `(14 : UInt16)`            |
| `UInt`   | 32 位无符号整数            | `14U`                      |
| `UInt64` | 64 位无符号整数            | `14UL`                     |
| `Double` | 64 位浮点数，由 IEEE754 定义 | `3.14`                     |
| `Float`  | 32 位浮点数              | `(3.14 : Float)`           |
| `BigInt` | 表示比其他类型更大的数值         | `10000000000000000000000N` |

MoonBit 还支持数字字面量，包括十进制、二进制、八进制和十六进制数字。

为了提高可读性，您可以在数字字面量中间放置下划线，例如 `1_000_000`。请注意，下划线可以放在数字中的任何位置，而不仅仅是每三位数字。

- 十进制数之间可以有下划线。

  默认情况下，整数字面量是有符号的 32 位数字。对于无符号数字，需要后缀 `U`；对于 64 位数字，需要后缀 `L`。
  ```moonbit
  let a = 1234
  let b : Int = 1_000_000 + a
  let unsigned_num       : UInt   = 4_294_967_295U
  let large_num          : Int64  = 9_223_372_036_854_775_807L
  let unsigned_large_num : UInt64 = 18_446_744_073_709_551_615UL
  ```
- 二进制数有一个前导零，后跟字母 "B"，即 `0b`/`0B`。请注意，`0b`/`0B` 后的数字必须是 `0` 或 `1`。
  ```moonbit
  let bin = 0b110010
  let another_bin = 0B110010
  ```
- 八进制数有一个前导零，后跟字母 "O"，即 `0o`/`0O`。请注意，`0o`/`0O` 后的数字必须在 `0` 到 `7` 的范围内：
  ```moonbit
  let octal = 0o1234
  let another_octal = 0O1234
  ```
- 十六进制数有一个前导零，后跟字母 "X"，即 `0x`/`0X`。请注意，`0x`/`0X` 后的数字必须在 `0123456789ABCDEF` 范围内。
  ```moonbit
  let hex = 0XA
  let another_hex = 0xA_B_C
  ```
- 浮点数字面量是 64 位浮点数。要定义一个浮点数，需要类型注释。
  ```moonbit
  let double = 3.14 // Double
  let float : Float = 3.14
  let float2 = (3.14 : Float)
  ```

  64 位浮点数也可以使用十六进制格式定义：
  ```moonbit
  let hex_double = 0x1.2P3 // (1.0 + 2 / 16) * 2^(+3) == 9
  ```

当期望的类型已知时，MoonBit 可以自动重载字面量，无需通过字母后缀指定数字的类型：

```moonbit
let int : Int = 42
let uint : UInt = 42
let int64 : Int64 = 42
let double : Double = 42
let float : Float = 42
let bigint : BigInt = 42
```

##### SEE ALSO
[重载字面量]()

#### 字符串

`String` 包含一系列 UTF-16 码点。您可以使用双引号创建字符串，或使用 `#|` 编写多行字符串。

```moonbit
let a = "兔rabbit"
debug_inspect(a.code_unit_at(0).to_char(), content="Some('兔')")
debug_inspect(a.code_unit_at(1).to_char(), content="Some('r')")
let b =
  #| Hello
  #| MoonBit\n
  #|
println(b)
```

```default
 Hello
 MoonBit\n

```

在双引号字符串中，反斜杠后跟某些特殊字符形成转义序列：

| 转义序列                   | 描述             |
|------------------------|----------------|
| `\n`, `\r`, `\t`, `\b` | 换行，回车，水平制表符，退格 |
| `\\`                   | 反斜杠            |
| `\u5154` , `\u{1F600}` | Unicode 转义序列   |

MoonBit 支持字符串插值。它允许您在插值字符串中替换变量。此功能通过直接将变量值嵌入文本来简化构建动态字符串的过程。用于字符串插值的变量必须实现 [`Show` 特征](methods.md#builtin-traits)。

```moonbit
let x = 42
println("The answer is \{x}")
```

##### NOTE
插值表达式不能包含换行符、`{}` 或 `"`。

多行字符串可以使用前缀 `#|` 或 `$|` 定义，其中前者将保留原始字符串，后者将执行转义和插值：

```moonbit
let lang = "MoonBit"
let raw =
  #| Hello
  #| ---
  #| \{lang}
  #| ---
let interp =
  $| Hello
  $| ---
  $| \{lang}
  $| ---
println(raw)
println(interp)
```

```default
 Hello
 ---
 \{lang}
 ---
 Hello
 ---
 MoonBit
 ---
```

避免在同一个多行字符串中混用 `$|` 和 `#|`；请在整个块中选择一种风格。

[VSCode 扩展](../toolchain/vscode/index.md#actions) 包含一个操作，可将粘贴的文档转换为普通的多行字符串，并在纯文本与 MoonBit 多行字符串之间切换。

当期望类型是 `String` 时，数组字面量被重载为字符串。你可以通过单独列出每个字符来构造`String`。

```moonbit
test {
  let c : Char = '中'
  let s : String = [c, '文']
  inspect(s, content="中文")
}
```

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/string](https://mooncakes.io/docs/moonbitlang/core/string)

[重载字面量]()

#### 字符

`Char` 表示一个 Unicode 码点。

```moonbit
let a : Char = 'A'
let b = '兔'
let zero = '\u{30}'
let zero = '\u0030'
```

当期望类型是 `Int` 或 `UInt16` 时，字符字面量可以重载为对应类型：

```moonbit
test {
  let s : String = "hello"
  let b : UInt16 = s.code_unit_at(0) // 'h'
  assert_eq(b, 'h') // 'h' 被重载为 UInt16
  let c : Int = '兔'
  // 不可：超出范围
  // let d : UInt16 = '𠮷'
}

```

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/char](https://mooncakes.io/docs/moonbitlang/core/char)

[重载字面量]()

#### 字节

MoonBit 中的字节字面量是一个 ASCII 字符或一个转义序列，语法形式为 `b'...'`。字节字面量的类型是 `Byte`。例如：

```moonbit
fn main {
  let b1 : Byte = b'a'
  println(b1.to_int())
  let b2 = b'\xff'
  println(b2.to_int())
}
```

```default
97
255
```

`Bytes` 是不可修改的字节序列。与字节类似，字节序列字面量的形式是 `b"..."`。例如：

```moonbit
test {
  let b1 : Bytes = b"abcd"
  let b2 = b"\x61\x62\x63\x64"
  assert_eq(b1, b2)
}
```

`Byte`字面量和`Bytes`字面量也支持转义序列，但是和字符串字面量的转义序列有些不同。下面的表格列出了支持的转义序列：

| 转义序列                   | 描述             |
|------------------------|----------------|
| `\n`, `\r`, `\t`, `\b` | 换行，回车，水平制表符，退格 |
| `\\`                   | 反斜杠            |
| `\x41`                 | 十六进制转义序列       |
| `\o102`                | 八进制转义序列        |

##### NOTE
你可以通过向`@buffer.T`写入不同类型的数据来构造一个`Bytes`。例如：

```moonbit
test "buffer 1" {
  let buf : @buffer.Buffer = Buffer()
  buf.write_bytes(b"Hello")
  buf.write_byte(b'!')
  assert_eq(buf.contents(), b"Hello!")
}
```

数组字面量也可以通过指定序列中的每个字节，重载为 `Bytes` 序列。

```moonbit
test {
  let b : Byte = b'\xFF'
  let bs : Bytes = [b, b'\x01']
  inspect(
    bs,
    content=(
      #|b"\xff\x01"
    ),
  )
}
```

##### SEE ALSO
`Byte` 的 API：[https://mooncakes.io/docs/moonbitlang/core/byte](https://mooncakes.io/docs/moonbitlang/core/byte)<br />
\\\\
`Bytes` 的 API：[https://mooncakes.io/docs/moonbitlang/core/bytes](https://mooncakes.io/docs/moonbitlang/core/bytes)<br />
\\\\
`@buffer.T` 的 API：[https://mooncakes.io/docs/moonbitlang/core/buffer](https://mooncakes.io/docs/moonbitlang/core/buffer)

[重载字面量]()

##### 选择字节容器

MoonBit 有几种面向字节的容器类型。它们彼此相关，但用途并不相同：

| 类型                   | 所有权 / 可变性   | 可否扩容   | 典型用途                       |
|----------------------|-------------|--------|----------------------------|
| `Bytes`              | 拥有所有权，不可变   | 否      | 最终字节负载、API 边界、序列化后的数据      |
| `BytesView`          | 借用的不可变视图    | 否      | 在不复制的情况下切片或解析已有字节数据        |
| `Array[Byte]`        | 拥有所有权，可变    | 是      | 通用的可变字节存储                  |
| `FixedArray[Byte]`   | 拥有所有权，可变    | 否      | 定长工作缓冲区                    |
| `ArrayView[Byte]`    | 借用的数组视图     | 否      | 在不转移所有权的情况下传递基于数组的字节切片     |
| `MutArrayView[Byte]` | 借用的可变视图     | 否      | 原地修改借用的、基于数组的字节存储          |
| `@buffer.Buffer`     | 拥有所有权的可变构造器 | 是      | 逐步构造字节数据，最后调用 `contents()` |

有两个常见区别尤其重要：

- `Bytes` 与 `BytesView`：前者是拥有所有权的不可变数据，后者是借用的不可变切片。
- `Array[Byte]` 与 `ArrayView[Byte]` / `MutArrayView[Byte]`：前者是拥有所有权的可变存储，后者是其上的借用只读视图或可变视图。

当你需要显式表达这些约束时，可以使用对应的只读和可变视图类型：`ReadOnlyArray[Byte]` 与 `MutArrayView[Byte]`。模式匹配和位串解析同样适用于这些字节容器；参见 [数组模式]() 和 [位串模式]()。

#### 元组

元组是使用圆括号 `()` 构造的有限值集合，元素之间用逗号 `,` 分隔。元素的顺序很重要；例如，`(1,true)` 和 `(true,1)` 有不同的类型。以下是一个示例：

```moonbit
fn main {
  fn pack(
    a : Bool,
    b : Int,
    c : String,
    d : Double
  ) -> (Bool, Int, String, Double) {
    (a, b, c, d)
  }

  let quad = pack(false, 100, "text", 3.14)
  let (bool_val, int_val, str, float_val) = quad
  println("\{bool_val} \{int_val} \{str} \{float_val}")
}
```

```default
false 100 text 3.14
```

元组可以通过模式匹配或索引访问：

```moonbit
test {
  let t = (1, 2)
  let (x1, y1) = t
  let x2 = t.0
  let y2 = t.1
  assert_eq(x1, x2)
  assert_eq(y1, y2)
}
```

#### Ref

`Ref[T]` 是一个包含类型 `T` 的值 `val` 的可变引用。

可以使用 `{ val : x }` 构造它，并可以使用 `ref.val` 访问它。有关详细说明，请参见 [结构体]()。

```moonbit
let a : Ref[Int] = { val: 100 }

test {
  a.val = 200
  assert_eq(a.val, 200)
  a.val += 1
  assert_eq(a.val, 201)
}
```

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/ref](https://mooncakes.io/docs/moonbitlang/core/ref)

#### Option 和 Result

`Option` 和 `Result` 是 MoonBit 中表示可能的错误或失败的最常见类型。

- `Option[T]` 表示可能缺失的类型 `T` 的值。它可以缩写为 `T?`。
- `Result[T, E]` 表示类型 `T` 的值或类型 `E` 的错误。

有关详细说明，请参见 [枚举]()。

```moonbit
test {
  let a : Int? = None
  let b : Option[Int] = Some(42)
  let c : Result[Int, String] = Ok(42)
  let d : Result[Int, String] = Err("error")
  match a {
    Some(_) => assert_true(false)
    None => assert_true(true)
  }
  match d {
    Ok(_) => assert_true(false)
    Err(_) => assert_true(true)
  }
}
```

##### SEE ALSO
`Option` 的 API：[https://mooncakes.io/docs/moonbitlang/core/option](https://mooncakes.io/docs/moonbitlang/core/option)<br />
\\\\
`Result` 的 API：[https://mooncakes.io/docs/moonbitlang/core/result](https://mooncakes.io/docs/moonbitlang/core/result)

#### 数组

数组是使用方括号 `[]` 构造的有限值序列，元素之间用逗号 `,` 分隔。例如：

```moonbit
let numbers = [1, 2, 3, 4]
```

您可以使用 `numbers[x]` 来引用第 x 个元素。索引从零开始。

```moonbit
test {
  let numbers = [1, 2, 3, 4]
  let a = numbers[2]
  numbers[3] = 5
  let b = a + numbers[3]
  assert_eq(b, 8)
}
```

有 `Array[T]` 和 `FixedArray[T]`。视图类型为 `ArrayView[T]` 和 `MutArrayView[T]`（见下文）。

`Array[T]` 可以增长，`FixedArray[T]` 有固定的大小，因此需要使用初始值创建。

##### WARNING
一个常见的陷阱是使用相同的初始值创建 `FixedArray`：

```moonbit
test {
  let two_dimension_array = FixedArray::make(10, FixedArray::make(10, 0))
  two_dimension_array[0][5] = 10
  assert_eq(two_dimension_array[5][5], 10)
}
```

这是因为所有单元格引用相同的对象（在这种情况下是 `FixedArray[Int]`）。应该使用 `FixedArray::makei()`，它为每个索引创建一个对象。

```moonbit
test {
  let two_dimension_array = FixedArray::makei(10, fn(_i) {
    FixedArray::make(10, 0)
  })
  two_dimension_array[0][5] = 10
  assert_eq(two_dimension_array[5][5], 0)
}
```

当期望的类型已知时，MoonBit 可以自动重载数组，否则将创建 `Array[T]`：

```moonbit
let fixed_array_1 : FixedArray[Int] = [1, 2, 3]

let fixed_array_2 = ([1, 2, 3] : FixedArray[Int])

let array_3 : Array[Int] = [1, 2, 3] // Array[Int]
```

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/array](https://mooncakes.io/docs/moonbitlang/core/array)

[重载字面量]()

##### ArrayView

类似于其他语言中的 `slice`，视图是对集合的特定段的引用。您可以使用 `data[start:end]` 创建数组 `data` 的视图，引用从 `start` 到 `end`（不包括）的元素。`start` 和 `end` 索引都可以省略。

##### NOTE
`ArrayView` 本身是一个不可变的数据结构，但底层的 `Array` 或 `FixedArray` 可以被修改。需要可变视图时，请使用 `data.mut_view(...)` 获取 `MutArrayView[T]`。

```moonbit
test {
  let xs = [0, 1, 2, 3, 4, 5]
  let s1 : ArrayView[Int] = xs[2:]
  @test.assert_eq(s1.to_owned(), [2, 3, 4, 5])
  @test.assert_eq(xs[:4].to_owned(), [0, 1, 2, 3])
  @test.assert_eq(xs[2:5].to_owned(), [2, 3, 4])
  @test.assert_eq(xs[:].to_owned(), [0, 1, 2, 3, 4, 5])
  let mv : MutArrayView[Int] = xs.mut_view(start=1, end=3)
  mv[0] = 99
  inspect(xs[1], content="99")
}
```

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/array](https://mooncakes.io/docs/moonbitlang/core/array)

#### Map

MoonBit 在其标准库中提供了一个保留插入顺序的哈希映射数据结构，称为 `Map`。`Map` 可以通过方便的字面量语法创建：

```moonbit
let map : Map[String, Int] = { "x": 1, "y": 2, "z": 3 }
```

目前，映射字面量语法中的键必须是常量。`Map` 也可以通过模式匹配优雅地解构，参见 [Map 模式]()。

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/builtin#Map](https://mooncakes.io/docs/moonbitlang/core/builtin#Map)

[重载字面量]()

#### Json

MoonBit 通过重载字面量支持方便的 json 处理。当表达式的期望类型是 `Json` 时，数字、字符串、数组和映射字面量可以直接用于创建 json 数据：

```moonbit
let moon_pkg_json_example : Json = {
  "import": ["moonbitlang/core/builtin", "moonbitlang/core/coverage"],
  "test-import": ["moonbitlang/core/random"],
}
```

Json 值也可以进行模式匹配，参见 [Json 模式]()。

##### SEE ALSO
API: [https://mooncakes.io/docs/moonbitlang/core/json](https://mooncakes.io/docs/moonbitlang/core/json)

[重载字面量]()

### 重载字面量

通过重载字面量，你可以使用同一个语法来表示不同类型的值。例如，根据上下文期望的类型，使用`1`表示一个`UInt`或者`Double`。如果一个上下文中不能确定期望的类型，那么它默认为`Int`。

```moonbit
fn expect_double(x : Double) -> Unit {

}

test {
  let x = 1 // type of x is Int
  let y : Double = 1
  expect_double(1)
}
```

重载字面量可以组合使用。如果一个数组字面量能够被重载为`Bytes`类型，并且一个数字字面量可以被重载为`Byte`类型，那么`[1,2,3]`就可以被重载为`Bytes`。下面的表格列出了可以重载的字面量和规则：

| 重载字面量                           | 默认类型       | 可以被重载为                                                                            |
|---------------------------------|------------|-----------------------------------------------------------------------------------|
| `10`, `0xFF`, `0o377`, `10_000` | `Int`      | `UInt`, `Int64`, `UInt64`, `Int16`, `UInt16`, `Byte`, `Double`, `Float`, `BigInt` |
| `"str"`                         | `String`   | —                                                                                 |
| `'c'`                           | `Char`     | `Int`                                                                             |
| `3.14`                          | `Double`   | `Float`                                                                           |
| `[a, b, c]`（字面量 a、b、c 的类型为 E）   | `Array[E]` | `FixedArray[E]`、`String`（如果 E 的类型是 Char）、`Bytes`（如果 E 的类型是 Byte）                  |

在模式中也有类似的重载规则，更多细节见 [模式匹配]()。

##### NOTE
字面量重载不是值转换。如果要将一个变量转换为不同的类型的值，可以使用以`to_`开头的方法，例如`to_int()`和`to_double()`，等等。

#### 重载字面量中的转义序列

转义序列能在重载的`"..."`和`'...'`字面量中使用。转义序列的解释根据它被重载到的具体类型而定：

- 简单转义序列

  包括`\n`、`\r`、`\t`、`\\`和`\b`。这些转义序列在任何`"..."`和`'...'`字面量中都被支持。在期望的类型是`String`时，它们被解释为对应的字符；在期望的类型是`Bytes`时，它们被解释为对应的`Byte`。
- 字节转义序列

  `\x41`和`\o102`转义序列表示一个`Byte`值。支持在重载为`Bytes`和`Byte`类型的字面量中使用。
- Unicode 转义序列

  `\u5154`和`\u{1F600}`转义序列表示一个`Char`。支持在重载为`String`和`Char`类型的字面量中使用。

### 函数

函数接受参数并产生结果。在 MoonBit 中，函数是一等公民，这意味着函数可以是其他函数的参数或返回值。MoonBit 的命名约定要求函数名不应以大写字母（A-Z）开头。请参见下面的 `enum` 部分中的构造器。

#### 顶层函数

函数可以定义为顶级或局部。我们可以使用 `fn` 关键字定义一个顶级函数，它将三个整数相加并返回结果，如下所示：

```moonbit
fn add3(x : Int, y : Int, z : Int) -> Int {
  x + y + z
}
```

请注意，顶级函数的参数和返回值需要**显式**类型注释。

顶层函数和方法也可以使用 `declare` 引入。声明函数只有签名而没有函数体，后续实现必须与该签名匹配。当你想先提供 API 形状、稍后再放置实现时，这很有用。

```moonbit
declare fn declared_add(x : Int, y : Int) -> Int

fn declared_add(x : Int, y : Int) -> Int {
  x + y
}

struct DeclaredCounter(Int)

declare fn DeclaredCounter::value(self : Self) -> Int

fn DeclaredCounter::value(self : Self) -> Int {
  self.0
}

test "declared functions" {
  @test.assert_eq(declared_add(1, 2), 3)
  @test.assert_eq(DeclaredCounter(4).value(), 4)
}
```

如果声明函数有实现，声明和实现必须在函数名、可见性、类型参数、参数、返回类型和 effect 上保持一致。

#### 局部函数

局部函数可以是命名的或匿名的。局部函数定义可以省略类型注释：在大多数情况下，它们可以自动推断。例如：

```moonbit
fn local_1() -> Int {
  fn inc(x) { // 命名为 `inc`
    x + 1
  }
  // 匿名，立即应用于整数字面量 6
  (fn(x) { x + inc(2) })(6)
}

test {
  assert_eq(local_1(), 9)
}

```

MoonBit 为简单的匿名函数提供了一个非常简洁的箭头函数语法：

```moonbit
  [1, 2, 3].eachi((i, x) => println("\{i} => \{x}"))
  // 只有一个参数时可以省略括号
  [1, 2, 3].each(x => println(x * x))

```

尽管非顶层的函数支持自动推导参数和返回值的类型，只有箭头函数语法允许推导副作用。如果一个 `fn` 可能 [抛出错误](error-handling.md) 或 [进行异步操作](async-experimental.md)，那么这个 `fn` 必须有显式的 `raise` 或 `async` 标记。

函数，无论是命名的还是匿名的，都是  *词法闭包*：没有局部绑定的任何标识符必须引用来自周围词法范围的绑定。例如：

```moonbit
let global_y = 3

fn local_2(x : Int) -> (Int, Int) {
  fn inc() {
    x + 1
  }

  fn four() {
    global_y + 1
  }

  (inc(), four())
}

test {
  @test.assert_eq(local_2(3), (4, 4))
}
```

局部函数的定义内，可以使用这个函数自己或者之前定义的其他局部函数。如果要定义多个互相递归的局部函数，需要使用 `letrec f = .. and g = ..` 语法：

```moonbit
  fn f(x) {
    // `f` 可以调用它自己，但不能调用 `g`
    if x > 0 {
      f(x - 1)
    }
  }

  fn g(x) {
    // `g` 可以调用 `f` 和 `g` 自身
    if x < 0 {
      f(-x)
    } else {
      f(x)
    }
  }
  // 互递归的局部函数
  letrec even = x => x == 0 || odd(x - 1)
  and odd = x => x != 0 && even(x - 1)

```

#### 函数应用

函数可以应用于括号中的参数列表：

```moonbit
add3(1, 2, 7)
```

无论 `add3` 是一个使用名称定义的函数（如前面的示例）还是绑定到函数值的变量，都可以工作，如下所示：

```moonbit
test {
  let add3 = fn(x, y, z) { x + y + z }
  assert_eq(add3(1, 2, 7), 10)
}
```

表达式 `add3(1, 2, 7)` 返回 `10`。任何求值为函数值的表达式都是可应用的：

```moonbit
test {
  let f = fn(x) { x + 1 }
  let g = fn(x) { x + 2 }
  let w = (if true { f } else { g })(3)
  assert_eq(w, 4)
}
```

#### 部分应用

部分应用允许函数调用时只提供部分的参数，生成一个接受被余下的参数并返回结果的函数。在 MoonBit 中，通过`_`操作符可以对函数进行部分应用：

```moonbit
fn add(x : Int, y : Int) -> Int {
  x + y
}

test {
  let add10 : (Int) -> Int = x => add(10, x)
  println(add10(5)) // 输出 15
  println(add10(10)) // 输出 20
}

```

`_`操作符表示括号中缺少的参数。部分应用允许在一对括号中使用多个`_`。例如，`Array::fold(_, _, init=5)`等价于`fn(x, y) { Array::fold(x, y, init=5) }`。

`_`操作符也可以在创建枚举值、`self.f(args)`形式的函数调用和管道中使用。

#### 带标签的参数

**顶层**函数可以使用语法 `label~ : Type` 声明带标签的参数。`label` 也将作为函数体内的参数名：

```moonbit
fn labelled_1(arg1~ : Int, arg2~ : Int) -> Int {
  arg1 + arg2
}
```

可以通过语法 `label=arg` 提供带标签的参数。`label=label` 可以缩写为 `label~`：

```moonbit
test {
  let arg1 = 1
  assert_eq(labelled_1(arg2=2, arg1~), 3)
}
```

带标签的函数可以以任何顺序提供。参数的求值顺序与函数声明中参数的顺序相同。

#### 可选参数

可以通过语法 `label? : Type = default_expr` 使参数变为可选，其中 `default_expr` 可以省略。如果在调用处未提供该参数，则将使用默认表达式：

```moonbit
fn optional(opt? : Int = 42) -> Int {
  opt
}

test {
  assert_eq(optional(), 42)
  assert_eq(optional(opt=0), 0)
}
```

默认表达式每次使用时都会被求值。并且默认表达式中的副作用（如果有）也会被触发。例如：

```moonbit
fn incr(counter? : Ref[Int] = { val: 0 }) -> Ref[Int] {
  counter.val = counter.val + 1
  counter
}

test {
  @test.assert_eq(incr().val, 1)
  @test.assert_eq(incr().val, 1)
  let counter : Ref[Int] = { val: 0 }
  @test.assert_eq(incr(counter~).val, 1)
  @test.assert_eq(incr(counter~).val, 2)
}
```

可选参数在调用处就是普通表达式。在 `raise` 或 `async` 上下文中，你可以传入可能抛错或调用异步函数的表达式：

```moonbit
fn may_fail(x : Int) -> Int raise Failure {
  if x < 0 {
    fail("negative")
  }
  x
}

fn add_with_optional(base : Int, extra? : Int = 1) -> Int {
  base + extra
}

test {
  inspect(add_with_optional(1, extra=may_fail(2)), content="3")
}
```

对于 async 函数，可选参数表达式同样可以调用异步函数：

```moonbit

///|
async fn fetch_default() -> Int {
  ...
}

///|
async fn build(x? : Int = fetch_default()) -> Int {
  ...
}

///|
async fn use_value() -> Int {
  build(x=fetch_default())
}
```

如果要在不同的函数调用之间共享默认表达式的结果，可以将默认表达式提升到顶层 `let` 声明：

```moonbit
let default_counter : Ref[Int] = { val: 0 }

fn incr_2(counter? : Ref[Int] = default_counter) -> Int {
  counter.val = counter.val + 1
  counter.val
}

test {
  assert_eq(incr_2(), 1)
  assert_eq(incr_2(), 2)
}
```

默认表达式可以依赖于之前的参数，例如：

```moonbit
fn create_rectangle(a : Int, b? : Int = a) -> (Int, Int) {
  (a, b)
}

test {
  debug_inspect(create_rectangle(10), content="(10, 10)")
}
```

##### 没有默认值的可选参数

当用户未提供值时，具有不同语义的情况是很常见的。没有默认值的可选参数的类型为 `T?`，默认值为 `None`。在直接提供这种可选参数时，MoonBit 将自动用 `Some` 包装该值：

```moonbit
fn new_image(width? : Int, height? : Int) -> Image {
  if width is Some(w) {
    ...
  }
  ...
}

let img2 : Image = new_image(width=1920, height=1080)
```

有时，直接传递类型为 `T?` 的值也很有用，例如在转发可选参数时。MoonBit 为此提供了一个语法 `label?=value`，并且 `label?` 是 `label?=label` 的缩写：

```moonbit
fn image(width? : Int, height? : Int) -> Image {
  ...
}

fn fixed_width_image(height? : Int) -> Image {
  image(width=1920, height?)
}
```

<a id="autofill-arguments"></a>

#### 自动填充参数

MoonBit 支持在不同的调用位置自动填充特定类型的参数，例如函数调用的源位置。要声明一个自动填充参数，只需声明一个带标签的参数，并添加一个函数属性 `#callsite(autofill(param_a, param_b))`。现在，如果未显式提供参数，MoonBit 将在调用时自动填充它。

目前 MoonBit 支持两种类型的自动填充参数，`SourceLoc`，它是整个函数调用的源位置，以及 `ArgsLoc`，它是一个数组，包含每个参数的源位置（如果有）：

```moonbit
##callsite(autofill(loc, args_loc))
fn f(_x : Int, loc~ : SourceLoc, args_loc~ : ArgsLoc) -> String {
  (
    $|loc of whole function call: \{loc}
    $|loc of arguments: \{args_loc}
  )
  // 整个函数调用的位置：<filename>:7:3-7:10
  // 参数的位置：[Some(<filename>:7:5-7:6), Some(<filename>:7:8-7:9), None, None]
}

```

自动填充参数非常有用，用于编写调试和测试工具。

#### 函数别名

MoonBit 允许用户用别名来调用一个函数。声明函数别名的语法如下：

```moonbit
##alias(g)
##alias(h, visibility="pub")
fn k() -> Bool {
  true
}
```

你也可以通过字段 `visibility` 创建具有不同可见性的函数别名。

### 控制结构

#### 条件表达式

条件表达式由条件、结果和可选的 `else` 子句或 `else if` 子句组成。

```moonbit
if x == y {
  expr1
} else if x == z {
  expr2
} else {
  expr3
}
```

结果周围的大括号是必需的。

请注意，条件表达式在 MoonBit 中始终返回一个值，结果和 else 子句的返回值必须是相同的类型。以下是一个示例：

```moonbit
let initial = if size < 1 { 1 } else { size }
```

`else` 子句只有在返回值的类型为 `Unit`的时候省略。

#### 匹配表达式

`match` 表达式类似于条件表达式，但它使用 [模式匹配]() 来决定要评估哪个结果，并同时提取变量。

```moonbit
fn decide_sport(weather : String, humidity : Int) -> String {
  match weather {
    "sunny" => "tennis"
    "rainy" => if humidity > 80 { "swimming" } else { "football" }
    _ => "unknown"
  }
}

test {
  assert_eq(decide_sport("sunny", 0), "tennis")
}
```

如果省略了可能的条件，编译器将发出警告；如果真的出现该情况，程序将终止。

#### 卫语句

`guard` 语句用于检查指定的不变量。如果不变量的条件得到满足，程序将继续执行后续语句。如果条件不满足（即为假），则执行 `else` 块中的代码且返回其值（并跳过后续语句）。

```moonbit
fn guarded_get(array : Array[Int], index : Int) -> Int? {
  guard index >= 0 && index < array.length() else { None }
  Some(array[index])
}

test {
  debug_inspect(guarded_get([1, 2, 3], -1), content="None")
}
```

##### 卫语句与 is 表达式

`let` 语句可以与 [模式匹配]() 一起使用。但是，`let` 语句只能处理一种情况。使用 [is 表达式]() 配合卫语句可以解决这个问题。

在以下示例中，`getProcessedText` 假设输入的 `path` 指向的资源都是纯文本，并使用 `guard` 语句来在确保这个不变量的同时，解构出纯文本资源。与使用 `match` 语句相比，`text` 的后续处理可以少一级缩进。

```moonbit
enum Resource {
  Folder(Array[String])
  PlainText(String)
  JsonConfig(Json)
}

fn getProcessedText(
  resources : Map[String, Resource],
  path : String,
) -> String raise Error {
  guard resources.get(path) is Some(resource) else { fail("\{path} not found") }
  guard resource is PlainText(text) else { fail("\{path} is not plain text") }
  process(text)
}
```

如果省略了 `else` 部分，程序将在 `guard` 语句中指定的条件不为真或无法匹配时终止。

```moonbit
guard condition  // <=> guard condition else { panic() }
guard expr is Some(x)
// <=> guard expr is Some(x) else { _ => panic() }
```

#### While 循环

在 MoonBit 中，`while` 循环可用于在条件为真时重复执行一段代码块。在执行代码块之前，将评估条件。使用 `while` 关键字定义 `while` 循环，后跟条件和循环体。循环体是一系列语句。只要条件为真，就会执行循环体。

```moonbit
fn main {
  let mut i = 5
  while i > 0 {
    println(i)
    i = i - 1
  }
}
```

```default
5
4
3
2
1
```

循环体支持 `break` 和 `continue`。使用 `break` 可以退出当前循环，而使用 `continue` 则跳过当前迭代的剩余部分并继续下一次迭代。

```moonbit
fn main {
  let mut i = 5
  while i > 0 {
    i = i - 1
    if i == 4 {
      continue
    }
    if i == 1 {
      break
    }
    println(i)
  }
}
```

```default
3
2
```

`while` 循环还支持可选的 `nobreak` 子句。当循环条件变为假时，将执行 `nobreak` 子句，然后循环将结束。

```moonbit
fn main {
  let mut i = 2
  while i > 0 {
    println(i)
    i = i - 1
  } nobreak {
    println(i)
  }
}
```

```default
2
1
0
```

当有 `nobreak` 子句时，`while` 循环还可以返回一个值。返回值是 `nobreak` 子句的评估结果。在这种情况下，如果使用 `break` 退出循环，需要在 `break` 后提供一个返回值，该返回值应与 `nobreak` 子句的返回值类型相同。

```moonbit
fn main {
  let mut i = 10
  let r = while i > 0 {
    i = i - 1
    if i % 2 == 0 {
      break 5
    }
  } nobreak {
    7
  }
  println(r)
}
```

```default
5
```

```moonbit
fn main {
  let mut i = 10
  let r = while i > 0 {
    i = i - 1
  } nobreak {
    7
  }
  println(r)
}
```

```default
7
```

#### For 循环

MoonBit 还支持 C 风格的 For 循环。关键字 `for` 后跟由分号分隔的变量初始化子句、循环条件和更新子句。它们不需要用括号括起来。例如，下面的代码创建了一个新的变量绑定 `i`，它在整个循环中都有作用域且是不可变的。这使得编写清晰的代码并对其进行推理更容易：

```moonbit
fn main {
  for i = 0; i < 5; i = i + 1 {
    println(i)
  }
}
```

```default
0
1
2
3
4
```

变量初始化子句可以创建多个绑定：

```moonbit
for i = 0, j = 0; i + j < 100; i = i + 1, j = j + 1 {
  println(i)
}
```

应该注意，在更新子句中，当有多个绑定变量时，语义是同时更新它们。换句话说，在上面的示例中，更新子句不会按顺序执行 `i = i + 1`，`j = j + 1`，而是同时递增 `i` 和 `j`。因此，在更新子句中读取绑定变量的值时，总是会得到上一次迭代中更新的值。

变量初始化子句、循环条件和更新子句都是可选的。例如，以下两个是无限循环：

```moonbit
for i = 1; ; i = i + 1 {
  println(i)
}
for ;; {
  println("loop forever")
}
```

`for` 循环还支持 `continue`、`break` 和 `nobreak` 子句。与 `while` 循环一样，`for` 循环也可以使用 `break` 和 `nobreak` 子句返回一个值。

`continue` 语句跳过当前 `for` 循环的剩余部分（包括更新子句）并继续下一次迭代。`continue` 语句还可以更新 `for` 循环的绑定变量，只要后面跟着与绑定变量数量匹配的表达式，用逗号分隔。

例如，以下程序计算从 1 到 6 的偶数之和：

```moonbit
fn main {
  let sum = for i = 1, acc = 0; i <= 6; i = i + 1 {
    if i % 2 == 0 {
      println("even: \{i}")
      continue i + 1, acc + i
    }
  } nobreak {
    acc
  }
  println(sum)
}
```

```default
even: 2
even: 4
even: 6
12
```

#### `for .. in` 循环

MoonBit 支持通过 `for .. in` 循环语法遍历不同数据结构和序列的元素：

```moonbit
for x in [1, 2, 3] {
  println(x)
}
```

`for .. in` 循环被转换为在 MoonBit 标准库中使用 `Iter`。任何具有方法 `.iter() : Iter[T]` 的类型都可以使用 `for .. in` 进行遍历。有关 `Iter` 类型的更多信息，请参见下面的 [迭代器]()。

`for .. in` 循环还支持遍历整数序列，例如：

```moonbit
test {
  let mut i = 0
  for j in 0..<10 {
    i += j
  }
  assert_eq(i, 45)
  let mut k = 0
  for l in 0..<=10 {
    k += l
  }
  assert_eq(k, 55)
}
```

除了单个值的序列外，MoonBit 还支持通过 MoonBit 标准库中的 `Iter2` 类型遍历两个值的序列，例如 `Map`。任何具有方法 `.iter2() : Iter2[A, B]` 的类型都可以使用两个循环变量的 `for .. in` 进行遍历：

```moonbit
for k, v in { "x": 1, "y": 2, "z": 3 } {
  println(k)
  println(v)
}
```

另一个使用两个循环变量的 `for .. in` 的示例是在遍历数组时跟踪数组索引：

```moonbit
fn main {
  for index, elem in [4, 5, 6] {
    let i = index + 1
    println("The \{i}-th element of the array is \{elem}")
  }
}
```

```default
The 1-th element of the array is 4
The 2-th element of the array is 5
The 3-th element of the array is 6
```

`for .. in` 循环的主体支持诸如 `return`、`break` 和错误处理等控制流操作：

```moonbit
fn main {
  let map = { "x": 1, "y": 2, "z": 3, "w": 4 }
  for k, v in map {
    if k == "y" {
      continue
    }
    println("\{k}, \{v}")
    if k == "z" {
      break
    }
  }
}
```

```default
x, 1
z, 3
```

如果循环变量未使用，可以使用 `_` 忽略它。

#### 范围表达式与 `for .. in` 循环

`for .. in` 循环还能和范围表达式配合，用于遍历整数区间：

```moonbit
fn main {
  for x in 0..<5 {
    println(x)
  }
}
```

```default
0
1
2
3
4
```

`for .. in` 循环中一共有四种可用的范围表达式：

- `a..<b`：升序地从 `a` 遍历到 `b`（不包含 `b`）
- `a..<=b`：升序地从 `a` 遍历到 `b`（包含 `b`）
- `a>..b`：降序地从 `a` 遍历到 `b`（不包含 `a`）
- `a>=..b`：降序地从 `a` 遍历到 `b`（包含 `a`）

#### 列表推导式

MoonBit 支持列表推导式语法，可以通过遍历另一个集合或范围来构造集合：

```moonbit
let squares = [ for x in 1..<=5 => x * x ]
let even_numbers = [ for x in 0..<100 if x % 2 == 0 => x ]
let labelled = [ for i, x in ["a", "b", "c"] => "\{i}: \{x}" ]
let map = { 1: 2, 2: 4, 3: 8 }
let present = [ for x in [1, 2, 3] if map.get(x) is Some(y) => y ]
```

语法是 `[ for ... => ... ]`。`=>` 之前的部分遵循与 `for .. in` 相同的迭代规则：一个绑定变量使用 `Iter`，两个绑定变量使用 `Iter2`，并且支持 `0..<10` 这样的范围表达式。可选的 `if` 守卫会在求值结果表达式之前筛选元素。守卫中的 `is` 表达式引入的名称，例如上面的 `y`，可以在结果表达式中使用。

如果没有期望类型，结果默认是 `Array[T]`。如果期望类型已知，列表推导式也可以构造 `FixedArray[T]`、`ReadOnlyArray[T]`、`Iter[T]`、`String`、`Bytes` 或 `Json`：

```moonbit
let text : String = [ for x in 0..<3 => (x + 'a').unsafe_to_char() ]
let bytes : Bytes = [ for x in 0..<3 => x.to_byte() ]
let fixed : FixedArray[_] = [ for x in 1..<=3 => x ]
```

列表推导式也支持普通的 `for` 循环头形式。如果期望类型是 `Iter[T]`，循环不需要终止，因此可以用来定义无穷序列：

```moonbit
let fib_numbers : Iter[Int] = [
  for p1 = 1, p2 = 0;; p1 = p1 + p2, p2 = p1 => p1
]
let first_six = fib_numbers.take(6).collect()
```

列表推导式内部不允许使用 `return`、`break` 和 `continue` 等控制流操作。

#### 带标记的 Continue/Break

当一个循环被标记的时候，它可以从循环中的 `break` 或者 `continue` 中引用，例如：

```moonbit
test "break label" {
  let mut count = 0
  let xs = [1, 2, 3]
  let ys = [4, 5, 6]
  let res = outer~: for i in xs {
    for j in ys {
      count = count + i
      break outer~ j
    }
  } nobreak {
    -1
  }
  assert_eq(res, 4)
  assert_eq(count, 1)
}

test "continue label" {
  let mut count = 0
  let init = 10
  let res = outer~: for i = init {
    if i == 0 {
      break outer~ 42
    }
    for ;; {
      count = count + 1
      continue outer~ i - 1
    }
  }
  assert_eq(res, 42)
  assert_eq(count, 10)
}
```

#### `defer` 表达式

`defer` 表达式可以实现可靠的资源释放。`defer` 的语法如下

```moonbit
defer <expr>
<body>
```

当程序离开 `body` 时，`expr` 里的内容会被执行。例如，下面的程序：

```moonbit
  defer println("释放资源")
  println("使用资源")

```

会先输出 `使用资源`，然后输出 `释放资源`。无论 `body` 以何种方式退出，`defer` 表达式都会被执行。`defer` 能够处理 [错误](error-handling.md)，以及 `return`/`break`/`continue` 等控制流构造。

连续的 `defer` 会以倒序执行。例如，下面的程序：

```moonbit
  defer println("第一处 defer")
  defer println("第二处 defer")
  println("做些事情")

```

会先输出 `做些事情`，然后输出 `第二处 defer`，最后输出 `第一处 defer`

在 `defer` 右边的表达式里，不能使用 `return`/`break`/`continue`。目前，在 `defer` 右边的表达式里也不能抛出错误或调用 `async` 函数。

### 迭代器

迭代器是一个对象，它在遍历序列的同时提供对其元素的访问。传统的面向对象语言如 Java 的 `Iterator<T>` 使用 `next()` 和`hasNext()` 来遍历迭代过程，而函数式语言（JavaScript 的 `forEach`，Lisp 的 `mapcar`）提供了一个高阶函数，该函数接受一个操作和一个序列，然后使用该操作应用于序列。前者称为外部迭代器（对用户可见），后者称为内部迭代器（对用户不可见）。

内置类型 `Iter[T]` 是 MoonBit 的外部迭代器实现。它提供 `next()` 来拉取下一个值：返回 `Some(value)` 并推进迭代器，或在迭代结束时返回 `None`。几乎所有内置的顺序数据结构都已经实现了 `Iter`：

```moonbit
///|
fn filter_even(l : Array[Int]) -> Array[Int] {
  let l_iter : Iter[Int] = l.iter()
  l_iter.filter(x => (x & 1) == 0).collect()
}

///|
fn fact(n : Int) -> Int {
  let start = 1
  let range : Iter[Int] = start.until(n)
  range.fold(Int::mul, init=start)
}
```

常用的方法包括：

- `each`: 遍历迭代器中的每个元素，对每个元素应用某个函数。
- `fold`: 使用给定的函数，从给定的初始值开始，对迭代器的元素进行“折叠”。
- `collect`: 将迭代器的元素收集到一个数组中。
- `filter`: （惰性）根据谓词函数过滤迭代器的元素。
- `map`: （惰性）使用映射函数转换迭代器的元素。
- `concat`: （惰性）通过将第二个迭代器的元素附加到第一个迭代器，将两个迭代器合并为一个。

像 `filter` 和 `map` 这样的方法在序列对象（例如 Array）上非常常见。但是，`Iter` 的不同之处在于，任何构造新 `Iter` 的方法都是**惰性**的（即在调用时不会开始迭代，因为它被包装在一个函数内），因此不会为中间值分配内存。这就是使 `Iter` 优于遍历序列的原因：没有额外的成本。MoonBit 鼓励用户将 `Iter` 传递给函数，而不是传递序列对象本身。

预定义的序列结构如 `Array` 及其迭代器应该足够使用。但是，为了在自定义序列（元素类型为 `S`）中使用这些方法，我们需要实现 `Iter`，即返回 `Iter[S]` 的函数。以 `Bytes` 为例：

```moonbit
///|
fn iter(data : Bytes) -> Iter[Byte] {
  let mut index = 0
  Iter::new(fn() -> Byte? {
    if index < data.length() {
      let byte = data[index]
      index += 1
      Some(byte)
    } else {
      None
    }
  })
}
```

迭代器是单次遍历的：一旦调用 `next()` 或使用 `each`、`fold`、`collect` 等方法消耗它们，其内部状态就会推进，无法重置。如果需要再次遍历序列，请从源头重新获取一个 `Iter`。

### 自定义数据类型

创建新数据类型有两种方法：`struct` 和 `enum`。

#### 结构体

在 MoonBit 中，结构体类似于元组，但其字段由字段名称索引。可以使用结构体字面量构造结构体，结构体字面量由一组带标签的值组成，并用大括号括起来。如果结构体的字段与类型定义完全匹配，那么结构体字面量的类型可以自动推断。可以使用点语法 `s.f` 访问字段。如果使用关键字 `mut` 标记字段为可变的，则可以为其分配新值。

```moonbit
struct User {
  id : Int
  name : String
  mut email : String
}
```

```moonbit
fn main {
  let u = User::{ id: 0, name: "John Doe", email: "john@doe.com" }
  u.email = "john@doe.name"
  //! u.id = 10
  println(u.id)
  println(u.name)
  println(u.email)
}
```

```default
0
John Doe
john@doe.name
```

##### 使用简写构造结构体

如果已经有一些变量，如 `name` 和 `email`，在构造结构体时重复这些名称是多余的。可以使用简写，它的行为完全相同：

```moonbit
let name = "john"
let email = "john@doe.com"
let u = User::{ id: 0, name, email }
```

如果没有其他具有相同字段的结构体，在构造结构体时添加结构体的名称是多余的：

```moonbit
let u2 = { id: 0, name, email }
```

##### 结构体更新语法

可以用这个语法来根据现有结构体创建一个新的结构体，但只更新部分字段。

```moonbit
fn main {
  let user = { id: 0, name: "John Doe", email: "john@doe.com" }
  let updated_user = { ..user, email: "john@doe.name" }
  println(
    (
      $|{ id: \{user.id}, name: \{user.name}, email: \{user.email} }
      $|{ id: \{updated_user.id}, name: \{updated_user.name}, email: \{updated_user.email} }
    ),
  )
}
```

```default
{ id: 0, name: John Doe, email: john@doe.com }
{ id: 0, name: John Doe, email: john@doe.name }
```

##### 给结构体自定义构造器

MoonBit 还支持为每个 `struct` 类型定义自定义构造器。构造器是一个特殊方法，可以通过结构体名调用来创建值。首先像往常一样定义结构体：

```moonbit
struct IntBox {
  value : Int
} derive(Debug)
```

随后应将构造器实现为一个与结构体类型同名的方法。其返回值必须是该结构体本身：

```moonbit
fn IntBox::IntBox(value : Int) -> IntBox {
  { value, }
}
```

当一个结构体定义了构造器，它就可以直接通过类型名字被构造：

```moonbit
  let box = IntBox(10)
  debug_inspect(box, content="{ value: 10 }")
```

构造器调用遵循构造器方法的签名，因此无标签参数可以写成熟悉的 `TypeName(value)` 形式。

构造器也可以像普通函数一样使用带标签参数和可选参数：

```moonbit
struct StructWithConstr {
  x : Int
  y : Int
} derive(Debug)
```

```moonbit
fn StructWithConstr::StructWithConstr(x~ : Int, y? : Int = x) -> StructWithConstr {
  { x, y }
}
```

```moonbit
  let s = StructWithConstr(x=1)
  debug_inspect(s, content="{ x: 1, y: 1 }")
```

由于结构体构造器由普通函数实现，因此它们也可以抛出错误：

```moonbit
suberror BuildError {
  NegativeInput
} derive(Debug)

struct Positive {
  value : Int
} derive(Debug)
```

```moonbit
fn Positive::Positive(x : Int) -> Positive raise BuildError {
  guard x >= 0 else { raise NegativeInput }
  { value: x }
}
```

```moonbit
  try Positive(10) catch {
    error => debug_inspect(error, content="NegativeInput")
  } noraise {
    value => debug_inspect(value, content="{ value: 10 }")
  }
  try Positive(-1) catch {
    error => debug_inspect(error, content="NegativeInput")
  } noraise {
    value => debug_inspect(value, content="{ value: -1 }")
  }
```

异步构造器使用 `async fn TypeName::TypeName` 声明，并且可以在异步代码中使用：

```moonbit
struct AsyncBox {
  value : Int
} derive(Debug)
```

```moonbit
async fn AsyncBox::AsyncBox(x : Int) -> AsyncBox {
  @async.sleep(0)
  { value: x }
}
```

```moonbit
async test "struct constructor async" {
  let box = AsyncBox(10)
  debug_inspect(box, content="{ value: 10 }")
}
```

通过 `struct` 构造器创建值与 [枚举构造器]() 具有完全相同的语义，区别在于 `struct` 构造器不能用于模式匹配。例如，通过构造器创建来自外部包的 `struct` 时，如果表达式的期望类型已知，就可以省略包名。

由于 `struct` 构造器由普通函数实现，因此它们可以 [抛出错误](error-handling.md) 或 [进行异步操作](async-experimental.md)。`struct` 构造器也支持 [可选参数]()。可选参数的默认值写在构造器实现上，就像普通函数签名一样。

#### 枚举

枚举类型类似于函数式语言中的代数数据类型。熟悉 C/C++ 的用户可能更喜欢称其为标记联合。

枚举可以有一组情况（构造函数）。构造函数的名称必须以大写字母开头。可以使用这些名称来构造枚举的相应情况，或在模式匹配中检查枚举值属于哪个分支：

```moonbit
/// 一个枚举类型，表示两个值之间的顺序关系，
/// 有三种情况 "Smaller"、"Greater" 和 "Equal"
enum Relation {
  Smaller
  Greater
  Equal
}

```

```moonbit
/// 比较两个整数之间的顺序关系
fn compare_int(x : Int, y : Int) -> Relation {
  if x < y {
    // 当创建一个枚举时，如果目标类型已知，
    // 可以直接写构造函数名称
    Smaller
  } else if x > y {
    // 但是当目标类型未知时，
    // 你总是可以使用 `TypeName::Constructor` 来创建一个枚举
    Relation::Greater
  } else {
    Equal
  }
}

/// 输出一个类型为 `Relation` 的值
fn print_relation(r : Relation) -> Unit {
  // 使用模式匹配来决定 `r` 属于哪种情况
  match r {
    // 在模式匹配期间，如果类型已知，
    // 写构造函数的名称就足够了
    Smaller => println("smaller!")
    // 但是你也可以在模式匹配中使用 `TypeName::Constructor` 语法
    Relation::Greater => println("greater!")
    Equal => println("equal!")
  }
}

```

```moonbit
fn main {
  print_relation(compare_int(0, 1))
  print_relation(compare_int(1, 1))
  print_relation(compare_int(2, 1))
}
```

```default
smaller!
equal!
greater!
```

枚举情况也可以携带额外数据。以下是使用枚举定义整数列表类型的示例：

```moonbit
enum Lst {
  Nil
  // 构造函数 `Cons` 携带额外的数据：列表的第一个元素，
  // 和列表的其余部分
  Cons(Int, Lst)
}

```

```moonbit
// 除了将额外数据绑定到变量之外，
// 你还可以继续匹配构造函数内部的额外数据。
// 以下是一个函数，用于判断列表是否只包含一个元素
fn is_singleton(l : Lst) -> Bool {
  match l {
    // 此分支仅匹配形状为 `Cons(_, Nil)` 的值，
    // 即长度为 1 的列表
    Cons(_, Nil) => true
    // 使用 `_` 匹配其他所有情况
    _ => false
  }
}

fn print_list(l : Lst) -> Unit {
  // 在模式匹配带有额外数据的枚举时，
  // 除了决定值属于哪种情况
  // 你还可以提取该情况内部的额外数据
  match l {
    Nil => println("nil")
    // 这里 `x` 和 `xs` 定义了新变量
    // 而不是引用现有变量，
    // 如果 `l` 是一个 `Cons`，那么 `Cons` 的额外数据
    // （第一个元素和列表的其余部分）
    // 将绑定到 `x` 和 `xs
    Cons(x, xs) => {
      println("\{x},")
      print_list(xs)
    }
  }
}

```

```moonbit
fn main {
  // 使用 `Cons` 创建值时，必须提供 `Cons` 的额外数据
  let l : Lst = Cons(1, Cons(2, Nil))
  println(is_singleton(l))
  print_list(l)
}

```

```default
false
1,
2,
nil
```

##### 构造器与带标签参数

枚举构造器可以有带标签的参数：

```moonbit
enum E {
  // `x` 和 `y` 是有标签参数
  C(x~ : Int, y~ : Int)
}

```

```moonbit
// 使用有标签参数的构造函数进行模式匹配
fn f(e : E) -> Unit {
  match e {
    // `label=pattern`
    C(x=0, y=0) => println("0!")
    // `x~` 是 `x=x` 的缩写
    // 未匹配的有标签参数可以通过 `..` 省略
    C(x~, ..) => println(x)
  }
}

```

```moonbit
fn main {
  f(C(x=0, y=0))
  let x = 0
  f(C(x~, y=1)) // <=> C(x=x, y=1)
}
```

```default
0!
0
```

也可以像在模式匹配中访问结构体字段一样访问构造函数的有标签参数：

```moonbit
enum Object {
  Point(x~ : Double, y~ : Double)
  Circle(x~ : Double, y~ : Double, radius~ : Double)
}

suberror NotImplementedError derive(Debug)

fn Objecct::distance_with(
  self : Object,
  other : Object,
) -> Double raise NotImplementedError {
  match (self, other) {
    // 对于通过 `Point(..) as p` 定义的变量，
    // 编译器知道它必须是构造函数 `Point`，
    // 因此可以通过 `p.x`、`p.y` 等直接访问 `Point` 的字段。
    (Point(_) as p1, Point(_) as p2) => {
      let dx = p2.x - p1.x
      let dy = p2.y - p1.y
      (dx * dx + dy * dy).sqrt()
    }
    (Point(_), Circle(_)) | (Circle(_), Point(_)) | (Circle(_), Circle(_)) =>
      raise NotImplementedError
  }
}


```

```moonbit
fn main {
  let p1 : Object = Point(x=0, y=0)
  let p2 : Object = Point(x=3, y=4)
  let c1 : Object = Circle(x=0, y=0, radius=2)
  try {
    println(p1.distance_with(p2))
    println(p1.distance_with(c1))
  } catch {
    _ => println("NotImplementedError")
  }
}
```

```default
5
NotImplementedError
```

##### 构造器与可变字段

也可以为构造器定义可变字段。这对于定义命令式数据结构特别有用：

```moonbit
// 使用可变二叉搜索树实现的集合。
struct Set[X] {
  mut root : Tree[X]
}

fn[X : Compare] Set::insert(self : Set[X], x : X) -> Unit {
  self.root = self.root.insert(x, parent=Nil)
}

// 带有亲指针的可变二叉搜索树
enum Tree[X] {
  Nil
  // 只有带标签的参数可以是可变的
  Node(
    mut value~ : X,
    mut left~ : Tree[X],
    mut right~ : Tree[X],
    mut parent~ : Tree[X]
  )
}

// 将一个新元素插入到二叉搜索树中。
// 返回新的树
fn[X : Compare] Tree::insert(
  self : Tree[X],
  x : X,
  parent~ : Tree[X],
) -> Tree[X] {
  match self {
    Nil => Node(value=x, left=Nil, right=Nil, parent~)
    Node(_) as node => {
      let order = x.compare(node.value)
      if order == 0 {
        // 修改构造器的字段
        node.value = x
      } else if order < 0 {
        // 在这里创建的 `node` 和 `node.left` 之间的循环
        node.left = node.left.insert(x, parent=node)
      } else {
        node.right = node.right.insert(x, parent=node)
      }
      // 树不为空，所以新的树就是原来的树
      node
    }
  }
}

```

##### 可扩展枚举

`extenum` 定义的是开放的枚举类型。与普通的 `enum` 不同，`extenum` 之后可以继续接收更多构造器，包括来自其他包的构造器。当某个包想定义共享的事件、消息或扩展点类型，而其他包贡献各自的分支时，这会很有用。

```moonbit
pub(all) extenum LogEvent[T] {
  Info(T)
}
```

使用 `extenum Type += { ... }` 为同一个包中的可扩展枚举添加构造器：

```moonbit
pub(all) extenum LogEvent[T] += {
  Warning(T)
  Critical(T, T)
}
```

要扩展另一个包中的可扩展枚举，需要用定义该类型的包来限定目标类型：

```moonbit
pub(all) extenum @base.LogEvent[T] += {
  Debug(T)
}
```

可扩展枚举的构造器由定义该构造器的包来限定。对于来自当前包的构造器，当预期类型已知时，可以直接使用构造器名称。对于来自其他包的构造器，在表达式和模式中使用 `@pkg.Constructor`。当你想同时显式写出可扩展枚举类型和构造器来源时，可以将构造器写成 `@type_pkg.Type::@constructor_pkg.Constructor`。

当一个包同时导入基础包和扩展包时，来自这两个包的值具有同一个可扩展枚举类型：

```moonbit
pub fn describe(event : @base.LogEvent[String]) -> String {
  match event {
    @base.Info(message) => "info: \{message}"
    @base.Warning(message) => "warning: \{message}"
    @base.Critical(code, message) => "critical \{code}: \{message}"
    @plugin.Debug(message) => "debug: \{message}"
    _ => "unknown"
  }
}

pub fn debug_event(message : String) -> @base.LogEvent[String] {
  @plugin.Debug(message)
}

pub fn qualified_debug_event(message : String) -> @base.LogEvent[String] {
  @base.LogEvent::@plugin.Debug(message)
}
```

模式匹配必须包含通配符分支，因为当前声明之外还可以继续添加更多构造器。

只有 `extenum` 声明可以被扩展。普通的 `enum` 声明是封闭的。

#### 元组结构体

MoonBit 支持一种特殊的结构体称为元组结构体：

```moonbit
struct UserId(Int)

struct UserInfo(UserId, String)
```

元组结构体类似于只有一个构造函数的枚举（与元组结构体本身的名称相同）。因此，可以使用构造函数创建或使用模式匹配提取底层表示：

```moonbit
fn main {
  let id : UserId = UserId(1)
  let name : UserInfo = UserInfo(id, "John Doe")
  let UserId(uid) = id // uid : Int
  let UserInfo(_, uname) = name // uname: String
  println(uid)
  println(uname)
}
```

```default
1
John Doe
```

除了模式匹配之外，还可以使用索引访问元素，类似于元组：

```moonbit
fn main {
  let id : UserId = UserId(1)
  let info : UserInfo = UserInfo(id, "John Doe")
  let uid : Int = id.0
  let uname : String = info.1
  println(uid)
  println(uname)
}
```

```default
1
John Doe
```

#### 类型别名

MoonBit 支持使用语法 `type NewType = OldType` 定义类型别名：

##### WARNING
旧语法 `typealias OldType as NewType` 可能会在将来被移除。

```moonbit
pub type Index = Int
pub type MyIndex = Int
pub type MyMap = Map[Int, String]
```

与上面所有其他类型声明不同，类型别名不定义新类型，它只是一个行为与其定义完全相同的类型宏。因此，例如，不能为类型别名定义新方法或实现特征。

#### 本地类型

MoonBit 支持在顶层函数的顶部声明结构体/枚举，这些类型仅在当前顶层函数中可见。这些本地类型可以使用顶层函数的泛型参数，但不能引入额外的泛型参数。本地类型可以使用 derive 派生方法，但不能手动定义额外的方法。例如：

```moonbit
fn[T : Debug] toplevel(x : T) -> Unit {
  enum LocalEnum {
    A(T)
    B(Int)
  } derive(Debug)
  struct LocalStruct {
    a : (String, T)
  } derive(Debug)
  struct LocalStructTuple(T) derive(Debug)
  ...
}
```

目前，本地类型不支持声明为错误类型。

### 模式匹配

模式匹配允许我们匹配特定模式并从数据结构中绑定数据。

#### 简单模式

我们可以将表达式与以下内容进行模式匹配：

- 字面量，例如布尔值、数字、字符、字符串等
- 常量
- 结构体
- 枚举
- 数组
- 键值对
- JSON

等等。我们可以定义标识符来绑定匹配的值，以便稍后使用。

```moonbit
const ONE = 1

fn match_int(x : Int) -> Unit {
  match x {
    0 => println("zero")
    ONE => println("one")
    value => println(value)
  }
}
```

我们可以使用 `_` 作为我们不关心的值的通配符，并使用 `..` 忽略结构体或枚举的剩余字段，或数组（参见 [数组模式](#array-pattern））。

```moonbit
struct Point3D {
  x : Int
  y : Int
  z : Int
}

fn match_point3D(p : Point3D) -> Unit {
  match p {
    { x: 0, .. } => println("on yz-plane")
    _ => println("not on yz-plane")
  }
}

enum Point[T] {
  Point2D(Int, Int, name~ : String, payload~ : T)
}

fn[T] match_point(p : Point[T]) -> Unit {
  match p {
    //! Point2D(0, 0) => println("2D origin")
    Point2D(0, 0, ..) => println("2D origin")
    Point2D(_) => println("2D point")
    _ => panic()
  }
}
```

我们可以使用 `as` 为某些模式命名，可以使用 `|` 一次匹配多个情况。在单个模式中，变量名只能绑定一次，并且在 `|` 模式的两侧应绑定相同的变量集。

```moonbit
match expr {
  //! Add(e1, e2) | Lit(e1) => ...
  Lit(n) as a => ...
  Add(e1, e2) | Mul(e1, e2) => ...
  ...
}
```

#### 数组模式

数组模式可以用来匹配以下类型以获取其对应的元素或视图（View）：

| 类型                                    | 元素   | 视图           |
|---------------------------------------|------|--------------|
| Array[T], ArrayView[T], FixedArray[T] | T    | ArrayView[T] |
| Bytes, BytesView                      | 字节   | BytesView    |
| String, StringView                    | 字符   | StringView   |

数组模式可以有以下形式：

- `[]`：匹配空数组
- `[pa, pb, pc]`：匹配长度为 3 的数组，并将其中的元素分别绑定到 `pa`, `pb`, `pc`
- `[pa, ..rest, pb]`：匹配至少有两个元素的数组，并将第一个元素绑定到`pa`，最后一个元素绑定到 `pb`，其余元素绑定到 `rest`。如果不需要其余元素，可以省略绑定 `rest`。在 `..` 部分前后允许任意数量的元素。由于 `..` 可以匹配不确定数量的元素，因此在数组模式中最多只能出现一次。

```moonbit
test {
  let ary = [1, 2, 3, 4]
  if ary is [a, b, .. rest] && a == 1 && b == 2 && rest.length() == 2 {
    inspect("a = \{a}, b = \{b}", content="a = 1, b = 2")
  } else {
    fail("")
  }
  guard ary is [.., a, b] else { fail("") }
  inspect("a = \{a}, b = \{b}", content="a = 3, b = 4")
}
```

数组模式提供了一种 Unicode 安全的方式来操作字符串，这意味着它在访问元素的时候不会跨越代码单元边界。例如，我们可以检查一个包含 Unicode 的字符串是否是回文：

```moonbit
test {
  fn palindrome(s : String) -> Bool {
    for view = s.view() {
      match view {
        [] | [_] => break true
        [a, .. rest, b] => if a == b { continue rest } else { break false }
      }
    }
  }

  inspect(palindrome("abba"), content="true")
  inspect(palindrome("中b中"), content="true")
  inspect(palindrome("文bb中"), content="false")
}
```

当数组模式中有连续的字符或字节常量时，可以使用模式展开 `..` 运算符将它们组合起来，使代码看起来更整洁。在这种情况下，`..` 后跟字符串或字节常量匹配确切数量的元素，因此它可以在数组模式中多次使用。

```moonbit
const NO : Bytes = b"no"

test {
  fn match_string(s : String) -> Bool {
    match s {
      [.. "yes", ..] => true // equivalent to ['y', 'e', 's', ..]
    }
  }

  fn match_bytes(b : Bytes) -> Bool {
    match b {
      [.. NO, ..] => false // equivalent to ['n', 'o', ..]
    }
  }
}
```

#### 位串模式

位串模式可用于匹配字节容器中的打包比特字段。它们支持 `BytesView`、`Bytes`、`Array[Byte]`、`FixedArray[Byte]`、`ReadOnlyArray[Byte]` 和 `ArrayView[Byte]`。使用带 `be`/`le` 后缀的显式位宽以明确端序。`be` 支持 1..64 位；`le` 仅支持按字节对齐的宽度（8 \* n），因为小端只对字节序有明确意义。没有 `..` 时，模式必须消费整个视图。

```moonbit
test {
  let packet : Bytes = b"\xD2\x10\x7F"
  let header : BytesView = packet[0:2]
  let (flag, kind, version, length) = match header {
    [u1be(flag), u3be(kind), u4be(version), u8be(length)] =>
      (flag, kind, version, length)
    _ => fail("bad header")
  }
  assert_eq(flag, 1)
  assert_eq(kind, 0b101)
  assert_eq(version, 0b0010)
  assert_eq(length, 16)
}
```

使用字面量位模式验证头部，并用 `..` 捕获剩余数据以供下一步解析。

```moonbit
test {
  let data : Bytes = b"\xF1\xAA\xBB"
  let view : BytesView = data[0:]
  let tag = match view {
    [u4be(0b1111), u4be(tag), .. rest] => {
      assert_eq(rest, b"\xAA\xBB"[0:])
      tag
    }
    _ => fail("bad prefix")
  }
  assert_eq(tag, 0b0001)
}
```

常见字节容器示例（注意 `MutArrayView` 需要切片）：

```moonbit
test {
  let b : Bytes = b"\x80"
  guard b is [u1be(1), ..] else { fail("Bytes") }

  let a : Array[Byte] = [b'\x80']
  guard a is [u1be(1), ..] else { fail("Array[Byte]") }

  let f : FixedArray[Byte] = [b'\x80']
  guard f is [u1be(1), ..] else { fail("FixedArray[Byte]") }

  let r : ReadOnlyArray[Byte] = [b'\x80']
  guard r is [u1be(1), ..] else { fail("ReadOnlyArray[Byte]") }

  let v : ArrayView[Byte] = a[:]
  guard v is [u1be(1), ..] else { fail("ArrayView[Byte]") }

  let mv : MutArrayView[Byte] = a.mut_view()
  guard mv[:] is [u1be(1), ..] else { fail("MutArrayView[Byte]") }
}
```

有符号模式使用二进制补码语义。例如，`u1be` 产生 `0` 或 `1`，而 `i1be` 产生 `0` 或 `-1`：

```moonbit
test {
  let bytes = b"\x80"
  let u : UInt = match bytes[:] {
    [u1be(u), ..] => u
    _ => fail("u1be")
  }
  let i : Int = match bytes[:] {
    [i1be(i), ..] => i
    _ => fail("i1be")
  }
  assert_eq(u, 1U)
  assert_eq(i, -1)
}
```

结果类型取决于位宽：

| 位宽               | 结果类型           |
|------------------|----------------|
| 1..32 位（`u`/`i`） | `UInt` / `Int` |
| 33..64 位（`u`）    | `UInt64`       |
| 33..64 位（`i`）    | `Int64`        |

#### 范围模式

对于内置整数类型和 `Char`，MoonBit 允许匹配值是否落在特定范围内。

范围模式的形式为 `a..<b` 或 `a..=b`，其中 `..<` 表示上限是排他的，`..=` 表示包含上限。`a` 和 `b` 可以是以下之一：

- 字面量
- 使用 `const` 声明的常量
- `_`，表示此模式在此侧没有限制

以下是一些示例：

```moonbit
const Zero = 0

fn sign(x : Int) -> Int {
  match x {
    _..<Zero => -1
    Zero => 0
    1..<_ => 1
  }
}

fn classify_char(c : Char) -> String {
  match c {
    'a'..='z' => "lowercase"
    'A'..='Z' => "uppercase"
    '0'..='9' => "digit"
    _ => "other"
  }
}
```

#### Map 模式

MoonBit 允许在类似 map 的数据结构上方便地进行匹配。在 map 模式内，`key : value` 语法将在 map 中存在 `key` 时匹配，并将 `key` 的值与模式 `value` 匹配。`key? : value` 语法将无论 `key` 是否存在都匹配，`value` 将与 `map[key]`（一个可选项）匹配。

```moonbit
match map {
  // 仅在 `map` 中存在 "b" 时匹配
  { "b": _, .. } => ...
  // 仅在 `map` 中不存在 "b" 且 "a" 存在于 `map` 时匹配。
  // 匹配时，将 `map` 中的 "a" 的值绑定到 `x`
  { "b"? : None, "a": x, .. } => ...
  // 编译器报告缺失的情况：{ "b"? : None, "a"? : None }
}

```

- 要使用 map 模式匹配数据类型 `T`，`T` 必须具有某种类型 `K` 和 `V` 的方法 `get(Self, K) -> Option[V]`（请参见 [方法和特征](methods.md)）。
- 目前，map 模式的键部分必须是字面量或常量
- Map 模式始终是开放的：未匹配的键会被静默忽略，并且需要添加 `..` 以显示这一点
- Map 模式将编译为高效的代码：每个键最多只会被获取一次

#### Json 模式

当匹配的值具有类型 `Json` 时，可以直接使用字面量模式，以及构造函数：

```moonbit
match json {
  { "version": "1.0.0", "import": [..] as imports, .. } => ...
  { "version": Number(i, ..), "import": Array(imports), .. } => ...
  ...
}
```

#### 守卫条件

模式匹配表达式中的每个分支都可以有一个守卫条件。守卫条件是一个布尔表达式，只有当该条件为真时，对应的分支才会被匹配。如果守卫条件为假，则跳过该分支并尝试下一个分支。例如：

```moonbit
fn guard_cond(x : Int?) -> Int {
  fn f(x : Int) -> Array[Int] {
    [x, x + 42]
  }

  match x {
    Some(a) if f(a) is [0, b] => a + b
    Some(b) => b
    None => -1
  }
}

test {
  assert_eq(guard_cond(None), -1)
  assert_eq(guard_cond(Some(0)), 42)
  assert_eq(guard_cond(Some(1)), 1)
}
```

注意，在检查所有模式是否都被匹配表达式覆盖时，不会考虑守卫条件。因此，您会看到以下情况的警告：

```moonbit
fn guard_check(x : Int?) -> Unit {
  match x {
    Some(a) if a >= 0 => ()
    Some(a) if a < 0 => ()
    None => ()
  }
}
```

##### WARNING
不鼓励在守卫条件中调用可能通过副作用改变被匹配的值的函数。在这种情况下，被改变的部分不会在后续模式中重新求值。请谨慎使用。

### 泛型

泛型在顶层函数和数据类型定义中受支持。可以在方括号内引入类型参数。我们可以重写上述数据类型 `List`，添加类型参数 `T` 以获得列表的通用版本。然后，我们可以定义列表上的通用函数，如 `map` 和 `reduce`。

```moonbit
///|
enum List[T] {
  Nil
  Cons(T, List[T])
}

///|
fn[S, T] List::map(self : List[S], f : (S) -> T) -> List[T] {
  match self {
    Nil => Nil
    Cons(x, xs) => Cons(f(x), xs.map(f))
  }
}

///|
fn[S, T] List::reduce(self : List[S], op : (T, S) -> T, init : T) -> T {
  match self {
    Nil => init
    Cons(x, xs) => xs.reduce(op, op(init, x))
  }
}
```

### 特殊语法

#### 管道

MoonBit 提供了方便的管道语法 `x |> f(y)` 和 `f <| x`，可以用来串联常规函数调用，或让嵌套的构建器风格代码更易读：

```moonbit
5 |> ignore // <=> ignore(5)
[] |> Array::push(5) // <=> Array::push([], 5)
1
|> add(5) // <=> add(1, 5)
|> x => { x + 1 }
|> ignore // <=> ignore(add(1, 5))
```

MoonBit 代码遵循 *数据优先*风格，也就是说，函数将它的“主题”放置在第一个参数的位置。所以，管道默认将左侧的值填入右侧的函数调用的第一个参数的位置。例如 `x |> f(y)`等价于`f(x,y)`。

你也可以使用`_`操作符改变`x`在函数`f`的调用中的插入位置，例如`x |> f(y, _)`, 这等价于`f(y,x)`。带标签的参数也是支持的。

管道操作符也可以连接到箭头函数。通过管道传入箭头函数时，函数体必须用花括号包裹，例如 `value |> x => { x + 1 }`。

反向管道运算符会把右侧表达式作为左侧调用的最后一个参数。例如，`f <| x` 等价于 `f(x)`，而 `f(a, b) <| c` 等价于 `f(a, b, c)`。这对于 DSL 风格的代码尤其有用，因为像 `div([text("hello")])` 这样的嵌套调用可以改写为 `div <| [text <| "hello"]`。

```moonbit
let page = div <| [
    text <| "hello",
    section("toolbar") <| fn() { [text <| "save", text <| "cancel"] },
  ]
inspect(
  page,
  content="div(text(hello), toolbar: div(text(save), text(cancel)))",
)
```

因为反向管道会附加最后一个参数，所以它也很适合最后一个参数是 lambda 的函数，从而可以写出类似 `section("toolbar") <| fn () { ... }` 这样的尾随 lambda 风格。

#### 级联运算符

级联运算符`..`用于连续对同一值执行一系列可变操作。语法如下：

```moonbit
let arr = []..append([1])
```

这里 `x..f()` 等价于 `{ x.f(); x }`。

考虑以下情况：对于具有诸如`write_string`，`write_char`，`write_object`等方法的`StringBuilder`类型，我们经常需要对同一`StringBuilder`值执行一系列操作：

```moonbit
let builder = StringBuilder::new()
builder.write_char('a')
builder.write_char('a')
builder.write_object(1001)
builder.write_string("abcdef")
let result = builder.to_string()
```

为了避免重复输入`builder`，其方法通常设计为返回`self`本身，允许使用`.`运算符链接操作。为了区分不可变和可变操作，在 MoonBit 中，对于所有返回`Unit`的方法，可以使用级联运算符进行连续操作，而无需修改方法的返回类型。

```moonbit
let result = StringBuilder::new()
  ..write_char('a')
  ..write_char('a')
  ..write_object(1001)
  ..write_string("abcdef")
  .to_string()
```

#### is 表达式

`is` 表达式测试值是否符合特定模式。它返回一个 `Bool` 值，并可以在期望布尔值的任何地方使用，例如：

```moonbit
fn[T] is_none(x : T?) -> Bool {
  x is None
}

fn start_with_lower_letter(s : String) -> Bool {
  s is ['a'..='z', ..]
}
```

通过 `is` 表达式绑定的标识符可以在以下的上下文中使用：

1. 在与表达式（`&&`）中：左侧表达式中绑定的标识符可以在右侧表达式中使用
   ```moonbit
   fn f(x : Int?) -> Bool {
     x is Some(v) && v >= 0
   }
   ```
2. 在 `if` 的第一个分支中：如果条件是一系列布尔表达式 `e1 && e2 && ...`，则可以在条件为真的分支中使用 `is` 表达式中绑定的标识符。
   ```moonbit
   fn g(x : Array[Int?]) -> Unit {
     if x is [v, .. rest] && v is Some(i) && i is (0..=10) {
       debug(v)
       println(i)
       debug(rest)
     }
   }
   ```
3. 下面举一个在 `guard` 中使用的情况：
   ```moonbit
   fn h(x : Int?) -> Unit {
     guard x is Some(v)
     println(v)
   }
   ```
4. 在 `while` 循环中的使用：
   ```moonbit
   fn i(x : Int?) -> Unit {
     let mut m = x
     while m is Some(v) {
       println(v)
       m = None
     }
   }
   ```

`is` 表达式只能接受一个简单模式，如果你需要通过 `as` 把模式绑定到某个变量上，需要加括号。比如：

```moonbit
fn j(x : Int) -> Int? {
  Some(x)
}

fn init {
  guard j(42) is (Some(a) as b)
  println(a)
  debug(b)
}
```

#### 正则字面量表达式

`re"..."` 是一个正则字面量表达式，其类型为 `Regex`。

正则字面量是普通表达式，因此可以存入局部绑定、作为参数传递、用作默认参数值，也可以定义为常量：

```moonbit
let r : Regex = re"a(b+)"
const IDENT_START : Regex = re"[A-Za-z_]"
const IDENT : Regex = IDENT_START + re"[A-Za-z0-9_]*"
```

`Regex` 值也可以通过 `+` 组合成序列，通过 `|` 组合成备选。在需要正则常量表达式的地方，例如 [`=~`]()，可以直接引用由正则字面量定义的具名 `const`。

与普通字符串字面量不同，正则字面量中的反斜杠不需要二次转义。例如，应写 `re"/\*"`，而不是 `re"/\\*"`。

```moonbit
const REGEX_IDENT_START = re"[A-Za-z_]"

const REGEX_IDENT_CONT = re"[A-Za-z0-9_]*"

const REGEX_AB : Regex = re"a" + re"b"

fn regex_default_arg(re? : Regex = re"abc") -> Bool {
  re.execute("zabc") is Some(_)
}

test {
  let regex : Regex = re"a(b+)"
  assert_true(regex.execute("abbb") is Some(_))
  assert_true(regex.execute("ac") is None)

  assert_true(REGEX_AB.execute("ab") is Some(_))
  assert_true(REGEX_AB.execute("ac") is None)
  assert_true(regex_default_arg())
}
```

非法的正则字面量会在编译期被拒绝。

正则字面量使用 MoonBit 的正则语法，支持的形式包括：

- 字面字符：普通字符匹配其自身
- 通配符：`.` 匹配任意单个字符，包括换行符
- 字符类：`[abc]`、`[^abc]`、`[a-z]`
- 字符类中的 POSIX 类：`[[:digit:]]`、`[[:alpha:]]`、`[[:space:]]`、`[[:word:]]`、`[[:xdigit:]]` 等
- 量词：`*`、`+`、`?`、`{n}`、`{n,}`、`{n,m}`
- 非贪婪量词：`*?`、`+?`、`??`、`{n}?`、`{n,}?`、`{n,m}?`
- 分组与分支：`( ... )`、`(?: ... )`、`(?<name> ... )`、`a|b`
- 断言：`^`、`$`、`\b`、`\B`
- 作用域修饰符：`(?i: ... )` 用于大小写不敏感匹配

转义规则按正则语义处理，而不是按字符串语义处理。常见转义包括 `\n`、`\r`、`\t`、`\f`、`\v`，以及 `\.`、`\(` 这类元字符转义，还有 Unicode 转义 `\uXXXX` / `\u{X...}`。如果要匹配字面量 `{`，请使用 `[{]`，不要写成 `\{`。这是为了给未来在正则字面量中支持插值预留语法空间，因为 `\{` 会和插值语法冲突。

还有一些重要语义和限制：

- `^` 和 `$` 是非多行锚点：只匹配整个输入的开头和结尾
- `\b` 和 `\B` 当前在正则字面量作为一等 `Regex` 值使用时可用；但在 [`=~`]() 这样的 `regex match expression` 常量上下文中暂不可用，未来这一限制预计会放宽。
- POSIX 字符类基于 ASCII
- 不支持 `\d`、`\D`、`\s`、`\S`、`\w` 和 `\W`。请改用 `[[:digit:]]`、`[^[:digit:]]`、`[[:space:]]`、`[^[:space:]]`、`[[:word:]]` 和 `[^[:word:]]`。
- `re"..."` 不支持 `\xHH` 字节转义；请改用 Unicode 转义或直接写普通字符。
- 不支持前瞻、后顾、反向引用和字符类集合运算
- 在字符类中，`-` 用于表示范围。若要匹配字面量连字符，请写 `\-`；不支持把 `-` 放在字符类开头或结尾来表示字面量。

具名捕获组（例如 `(?<id>[0-9]+)`）属于 `Regex` 值本身。它们可与 `Regex::execute` 和 `MatchResult::named_group` 等 API 配合使用，但不会自行引入 MoonBit 绑定变量。

当正则字面量作为一等 `Regex` 值使用时，`Regex::execute` 等操作采用 first-match 语义：它们返回从搜索位置开始找到的第一个匹配，不提供 longest-match 模式。

#### 正则匹配表达式

正则匹配表达式使用 `=~` 运算符，以正则常量表达式在 `StringView` 中进行搜索。这是一种较新的正则匹配形式，旨在取代实验性的 `lexmatch`。该表达式返回 `Bool`。

```moonbit
input =~ re"abc"
input =~ ((PREFIX + SUFFIX) as whole, before=head, after=tail)
input =~ (re"b", before~, after~)
```

右侧必须是正则常量表达式：可以是 `re"abc"` 这样的正则字面量、具名 `const`，或者由常量通过 `+`（拼接）、`|`（分支）和括号构造出来的表达式。不允许任意运行时值。

使用 `as` 绑定匹配到的子串。使用 `before` 和 `after` 将未匹配的前缀和后缀绑定为 `StringView`；`before~` 和 `after~` 是简写，分别绑定名为 `before` 和 `after` 的变量。

这和正则的具名捕获组是两回事。例如在 `re"(?<id>[0-9]+)"` 中，名称 `id` 属于正则引擎的捕获元数据，不是 MoonBit 绑定变量。如果你需要在 `=~` 中引入绑定，请使用 `as`，例如 `(re"(?<id>[0-9]+)" as digits)`。

和 `is` 一样，`=~` 引入的绑定可以用于相同的布尔流上下文，例如 `&&` 的右侧和 `if` 的 true 分支。正则匹配默认按搜索语义工作，因此 `"zabc!" =~ re"abc"` 的结果是 `true`。如果需要限制匹配必须发生在输入的开头或结尾，请使用 `^` 和 `$` 等锚点。

`=~` 同样采用 first-match 语义。它在设计上不会支持 longest-match 行为。

```moonbit
test {
  let input = " let_name = 42 "
  if (input =~ (
      (REGEX_IDENT_START + REGEX_IDENT_CONT) as ident,
      before=head,
      after=tail
    )) {
    assert_true(head is " ")
    assert_true(ident is "let_name")
    assert_true(tail is " = 42 ")
  } else {
    fail("expected identifier")
  }

  if ("abc" =~ (re"b", before~, after~)) {
    assert_true(before is "a")
    assert_true(after is "c")
  } else {
    fail("expected middle match")
  }

  let source : StringView = "abc"
  if (source =~ (re"." as ch, after=rest)) {
    assert_eq(ch, 'a')
    assert_true(rest is "bc")
  } else {
    fail("expected leading char")
  }

  assert_true("zabc!" =~ re"abc")
  assert_true(!("zabc!" =~ re"^abc"))
}
```

上面的例子中，`head`、`ident`、`tail`、`before`、`after` 和 `rest` 的类型都是 `StringView`。绑定变量 `ch` 的类型是 `Char`，因为 `re"."` 恰好匹配一个字符。

#### Lexmatch

##### WARNING
`lexmatch` 和 `lexmatch?` 已弃用。新代码请优先使用 [regex match expression]()。本节仅作为现有代码的参考保留。

`lexmatch` 用正则模式匹配 `String`，并让你绑定匹配到的各个片段。搜索模式的写法是 `(before, regex pieces, after)`，其中 `before` 和 `after` 是可选的绑定，用于捕获未匹配的前缀和后缀，并用逗号分隔；中间的正则片段只用空白分隔。正则本身由一串字符串字面量组成，因此可以拆成多行或在各部分之间插入注释。你也可以用 `as` 绑定子模式，例如 `("b*" as b)`。

`lexmatch?` 是类似 `is` 的布尔检查，并且可以在与 `is` 表达式相同的上下文中引入绑定。

在旧代码中，搜索模式的 `lexmatch` 写法如下：

```moonbit
lexmatch text {
  (before, "a" ("b*" as b) "c", after) => ...
  _ => ...
}

if text lexmatch? ("a" ("b*" as b) "c") && b.length() > 0 {
  ...
}
```

在新代码中，请改用 `=~` 编写这些搜索模式检查。

`lexmatch` 还支持类似词法器的模式：`lexmatch <expr> with longest`，它会在备选项中选择最长匹配（例如在 longest 模式下 `if|[a-z]*` 会把 `iff` 匹配为 `iff`，而首个匹配的搜索模式会先匹配到 `if`）。正则匹配表达式不提供这种最长匹配模式。

正则字面量支持把 `\\b` 和 `\\B` 作为正则语法的一部分使用，但这些单词边界断言目前在 `regex match expression` 的常量上下文中还不可用。当正则作为一等 `Regex` 值使用时，它们是可用的，未来这一限制预计会放宽。另外，正则字面量也不支持 `\\d`、`\\D`、`\\s`、`\\S`、`\\w` 和 `\\W`。请改用字符类中的 POSIX 字符类，例如 `[[:digit:]]`。

```moonbit
test {
  let text = "xxabbbcyy"
  if text =~ (re"a" + (re"b*" as b) + re"c", before~, after~) {
    inspect(before, content="xx")
    inspect(b, content="bbb")
    inspect(after, content="yy")
  } else {
    fail("")
  }

  if text =~ (re"a" + (re"b*" as b) + re"c") && b.length() > 0 {
    inspect(b, content="bbb")
  }

  let keyword = "iff"
  lexmatch keyword with longest {
    ("if|[a-z]*" as ident) => inspect(ident, content="iff")
    _ => fail("")
  }
}
```

#### 展开运算符

MoonBit 提供了一个展开运算符，用于在使用数组字面量语法构造 `Array`、`String`和 `Bytes` 时展开元素序列。要展开这样的序列，需要在其前面加上 `..` 前缀，并且该序列必须具有 `iter()` 方法，并且该方法能够产生相应类型的元素。

例如，我们可以使用展开运算符构造一个数组：

```moonbit
test {
  let a1 : Array[Int] = [1, 2, 3]
  let a2 : FixedArray[Int] = [4, 5, 6]
  let a3 : @list.List[Int] = @list.from_array([7, 8, 9])
  let a : Array[Int] = [..a1, ..a2, ..a3, 10]
  debug_inspect(a, content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]")
}
```

同样，我们可以使用展开运算符构造一个字符串：

```moonbit
test {
  let s1 : String = "Hello"
  let s2 : StringView = "World".view()
  let s3 : Array[Char] = [..s1, ' ', ..s2, '!']
  let s : String = [..s1, ' ', ..s2, '!', ..s3]
  inspect(s, content="Hello World!Hello World!")
}
```

最后一个例子展示了如何使用展开运算符构造一个字节序列。

```moonbit
test {
  let b1 : Bytes = b"hello"
  let b2 : BytesView = b1[1:4]
  let b : Bytes = [..b1, ..b2, 10]
  inspect(
    b,
    content=(
      #|b"helloell\x0a"
    ),
  )
}
```

#### TODO 语法

`todo`语法 (`...`) 是一种特殊构造，用于标记尚未实现或用于未来功能的占位符代码段。例如：

```moonbit
fn todo_in_func() -> Int {
  ...
}
```

<!-- path: language/methods.md -->
## 方法和特征

### 方法系统

MoonBit 支持方法的方式与传统的面向对象语言不同。MoonBit 中的方法只是与类型构造器关联的顶层函数。定义方法时，在函数名之前添加 `SelfTypeName::` 前缀，例如 `fn SelfTypeName::method_name(...)`，这样方法就属于 `SelfTypeName`。在方法的签名内，可以用 `Self` 来指代 `SelfTypeName`。

##### WARNING
目前，MoonBit 支持一种定义方法的简短语法。当某个函数定义的第一个参数的名字是 `self` 时，它会被视作 `self` 的类型上的方法定义。这一语法在未来可能会被废弃，我们不鼓励在新代码中使用这一写法。

```moonbit
fn method_name(self : SelfType) -> Unit { ... }
```

```moonbit
enum List[X] {
  Nil
  Cons(X, List[X])
}

///|
fn[X] List::length(xs : List[X]) -> Int {
  ...
}
```

要调用一个方法，可以使用语法 `T::method_name(..)`，或者当其中第一个参数是 `T` 的类型时使用点调用：

```moonbit
let l : List[Int] = Nil
println(l.length())
println(List::length(l))
```

当方法的第一个参数也是它所属的类型时，可以使用点语法 `x.method(...)` 调用方法。MoonBit 根据 `x` 的类型自动找到正确的方法，无需编写方法的类型名称甚至包名称：

```moonbit
pub(all) enum List[X] {
  Nil
  Cons(X, List[X])
}

pub fn[X] List::concat(list : List[List[X]]) -> List[X] {
  ...
}
```

```moonbit
// 假设 `xs` 是一个列表的列表，那么下面两种写法是等价的：
let _ = xs.concat()
let _ = @list.List::concat(xs)

```

用 `TypeName::method_name` 形式定义出的方法支持重载：由于不同类型的方法处于不同的命名空间中，不同的类型可以定义同名的方法。

```moonbit
struct T1 {
  x1 : Int
}

fn T1::default() -> T1 {
  { x1: 0 }
}

struct T2 {
  x2 : Int
}

fn T2::default() -> T2 {
  { x2: 0 }
}

test {
  let t1 = T1::default()
  let t2 = T2::default()

}
```

#### 本地方法

为了保证方法定义只有单一来源并规避歧义，[只能在类型所在的包里定义方法](packages.md#trait-implementations)。然而，这条规则有一个例外：MoonBit 允许给来自外部的类型定义  *私有* 方法。这些本地方法可以覆盖来自类型自己的包的方法（但此时 MoonBit 会报一个警告），为上游包的 API 提供拓展和补充：

```moonbit
fn Int::my_int_method(self : Int) -> Int {
  self * self + self
}

test {
  assert_eq((6).my_int_method(), 42)
}
```

#### 通过别名把方法变成函数

MoonBit 允许用户用别名来调用一个方法。声明方法别名的语法如下：

方法别名会创建一个同名的方法。你也可以选择创建一个同名的函数。别名的可见性也可以被控制。

```moonbit
##alias(m)
##alias(n, visibility="priv")
##as_free_fn(m)
##as_free_fn(n, visibility="pub")
fn List::f() -> Bool {
  true
}
test {
  assert_eq(List::f(), List::m())
  assert_eq(List::m(), m())
}
```

### 运算符重载

MoonBit 通过内建的特征支持中缀运算符的重载，例如：

```moonbit
struct T {
  x : Int
}

impl Add for T with add(self : T, other : T) -> T {
  { x: self.x + other.x }
}

test {
  let a = { x: 0 }
  let b = { x: 2 }
  assert_eq((a + b).x, 2)
}
```

其他运算符通过带有属性的方法重载，例如 `_[_]` 和 `_[_]=_`：

```moonbit
struct Coord {
  mut x : Int
  mut y : Int
}

##alias("_[_]")
fn Coord::get(coord : Self, key : String) -> Int {
  match key {
    "x" => coord.x
    "y" => coord.y
  }
}

##alias("_[_]=_")
fn Coord::set(coord : Self, key : String, val : Int) -> Unit {
  match key {
    "x" => coord.x = val
    "y" => coord.y = val
  }
}
```

```moonbit
fn main {
  let c = { x: 1, y: 2 }
  println("{x: \{c.x}, y: \{c.y}}")
  println(c["y"])
  c["x"] = 23
  println("{x: \{c.x}, y: \{c.y}}")
  println(c["x"])
}
```

```default
{x: 1, y: 2}
2
{x: 23, y: 2}
23
```

目前，可以重载以下运算符：

| 运算符名称           | 重载方式                    |
|-----------------|-------------------------|
| `+`             | 特征 `Add`                |
| `-`             | 特征 `Sub`                |
| `*`             | 特征 `Mul`                |
| `/`             | 特征 `Div`                |
| `%`             | 特征 `Mod`                |
| `==`            | 特征 `Eq`                 |
| `<<`            | 特征 `Shl`                |
| `>>`            | 特征 `Shr`                |
| `-`（一元）         | 特征 `Neg`                |
| `_[_]`（获取项）     | method + alias `_[_]`   |
| `_[_] = _`（设置项） | method + alias `_[_]=_` |
| `_[_:_]`（视图）    | method + alias `_[_:_]` |
| `&`             | 特征 `BitAnd`             |
| `|`             | 特征 `BitOr`              |
| `^`             | 特征 `BitXOr`             |

在重载 `_[_]`/`_[_] = _`/`_[_:_]` 时，定义的方法需要有正确的类型签名：

- `_[_]` 的签名应该形如 `(Self, Index) -> Result`，使用方式为 `let result = self[index]`
- `_[_]=_` 的签名应该形如 `(Self, Index, Value) -> Unit`，使用方式为 `self[index] = value`
- `_[_:_]` 的签名应当形如 `(Self, start? : Index, end? : Index) -> Result`，使用方式为 `let result = self[start:end]`

通过实现 `_[_:_]` 方法，可以为用户定义的类型创建视图。以下是一个例子：

```moonbit
struct DataView(String)

struct Data {}

##alias("_[_:_]")
fn Data::as_view(_self : Data, start? : Int = 0, end? : Int) -> DataView {
  "[\{start}, \{end.unwrap_or(100)})"
}

test {
  let data = Data::{  }
  inspect(data[:].0, content="[0, 100)")
  inspect(data[2:].0, content="[2, 100)")
  inspect(data[:5].0, content="[0, 5)")
  inspect(data[2:5].0, content="[2, 5)")
}
```

### Trait（特征）系统

MoonBit 具有用于重载/特殊多态的结构特征系统。特征声明一系列操作，当类型想要实现特征时，必须提供这些操作。特征可以如下声明：

```moonbit
pub(open) trait I {
  method_(Int) -> Int
  method_with_label(Int, label~ : Int) -> Int
  //! method_with_label(Int, label?: Int) -> Int
}
```

在特征定义的主体中，使用特殊类型 `Self` 来引用实现特征的类型。

#### 扩展特征

特征（子特征）可以依赖于其他特征（超特征），例如：

```moonbit
pub(open) trait Position {
  pos(Self) -> (Int, Int)
}

pub(open) trait Draw {
  draw(Self, Int, Int) -> Unit
}

pub(open) trait Object: Position + Draw {}
```

#### 实现特征

如果某类型想要实现一个特征，它需要显式地实现特征中的所有方法。实现特征方法的语法是 `impl Trait for Type with method_name(...) { ... }`，例如：

```moonbit
pub(open) trait MyShow {
  to_string(Self) -> String
}

struct MyType {}

pub impl MyShow for MyType with to_string(self) {
  ...
}

struct MyContainer[_] {}

/// 使用类型参数实现特征。
/// `[X : Show]` 意味着类型参数 `X` 必须实现 `Show`，
/// 我们将稍后介绍。
pub impl[X : MyShow] MyShow for MyContainer[X] with to_string(self) {
  ...
}

```

`impl` 实现的类型注释可以省略：MoonBit 将根据 `Trait::method` 的签名和 self 类型自动推断类型。

特征的作者还可以为特征中的某些方法定义**默认实现**，例如：

```moonbit
pub(open) trait J {
  f(Self) -> Unit
  f_twice(Self) -> Unit = _
}

impl J with f_twice(self) {
  self.f()
  self.f()
}
```

注意除了实际的默认实现 `impl J with f_twice` 外，在 `J` 中、`f_twice` 的声明里，还需要提供一个 `= _` 标记。这一标记能让代码的读者一眼知道哪些方法有默认实现，改善可读性。

`J` 的类型实现特征时不必为 `f_twice` 提供实现：要实现 `J`，只有 `f` 是必要的。如果需要，他们总是可以显式地用 `impl J for Type with f_twice` 覆盖默认实现。

```moonbit
impl J for Int with f(self) {
  println(self)
}

impl J for String with f(self) {
  println(self)
}

impl J for String with f_twice(self) {
  println(self)
  println(self)
}

```

要实现子特征，必须实现超特征，以及子特征中的方法。

```moonbit
impl Position for Point with pos(self) {
  (self.x, self.y)
}

impl Draw for Point with draw(self, x, y) {
  ()
}

impl Object for Point

pub fn[O : Object] draw_object(obj : O) -> Unit {
  let (x, y) = obj.pos()
  obj.draw(x, y)
}

test {
  let p = Point::{ x: 1, y: 2 }
  draw_object(p)
}
```

即使一个特征的每个方法都有默认实现，也依然需要显式实现它，否则 [抽象特征](packages.md#traits) 等功能无法工作。为此，MoonBit 提供了 `impl Trait for Type` 语法（去除了方法部分，除此之外与前面的 `impl` 相同）`impl Trait for Type` 保证了 `Type` 会实现 `Trait`，MoonBit 会自动检查 `Trait` 中的每个方法是否都有对应的实现。

除了用于处理每个方法都有默认实现的特征，`impl Trait for Type` 还可以用作文档，或是在实际完成实现之前的一个待办标记。

##### WARNING
目前，没有任何方法的空特征会自动实现。

#### 使用特征

在声明泛型函数时，可以使用特征注释类型参数，来定义受约束的泛型函数。例如：

```moonbit
fn[X : Eq] contains(xs : Array[X], elem : X) -> Bool {
  for x in xs {
    if x == elem {
      return true
    }
  } nobreak {
    false
  }
}
```

如果没有 `Eq` 要求，`contains` 中的表达式 `x == elem` 将产生类型错误。现在，函数 `contains` 可以使用任何实现 `Eq` 的类型调用，例如：

```moonbit
struct Point {
  x : Int
  y : Int
}

impl Eq for Point with equal(p1, p2) {
  p1.x == p2.x && p1.y == p2.y
}

test {
  assert_false(contains([1, 2, 3], 4))
  assert_true(contains([1.5, 2.25, 3.375], 2.25))
  assert_false(contains([{ x: 2, y: 3 }], { x: 4, y: 9 }))
}
```

##### 直接调用特征方法

可以通过 `Trait::method` 直接调用特征的方法。MoonBit 将推断 `Self` 的类型，并检查 `Self` 是否确实实现了 `Trait`，例如：

```moonbit
test {
  assert_eq(Show::to_string(42), "42")
  assert_eq(Compare::compare(1.0, 2.5), -1)
}
```

特征实现也可以通过点语法调用，但有以下限制：

1. 如果存在常规方法，使用点语法时总是优先选择常规方法
2. 只有位于 self 类型的包中的特征实现才能通过点语法调用
   - 如果有多个具有相同名称的特征方法（来自不同的特征）可用，将报告歧义错误

上述规则确保了 MoonBit 的点语法具有良好的特性，同时也具有灵活性。例如，由于歧义，添加新依赖关系永远不会破坏现有的点语法代码。这些规则还使 MoonBit 的名称解析非常简单：通过点语法调用的方法必须始终来自当前包或类型的包！

以下是使用点语法调用特征 `impl` 的示例：

```moonbit
struct MyCustomType {}

pub impl Show for MyCustomType with output(self, logger) {
  ...
}

fn f() -> Unit {
  let x = MyCustomType::{  }
  let _ = x.to_string()
}
```

### 特征对象

MoonBit 支持通过特征对象实现运行时多态。如果 `t` 是类型 `T`，它实现了特征 `I`，可以通过 `t as &I` 将实现 `I` 的 `T` 的方法与 `t` 一起打包到运行时对象中。如果从上下文可以知道某个表达式的类型是特征对象类型，则 `as &I` 可以省略。特征对象擦除了值的具体类型，因此可以将从不同具体类型创建的对象放入相同的数据结构并统一处理：

```moonbit
pub(open) trait Animal {
  speak(Self) -> String
}

struct Duck(String)

fn Duck::make(name : String) -> Duck {
  Duck(name)
}

impl Animal for Duck with speak(self) {
  "\{self.0}: quack!"
}

struct Fox(String)

fn Fox::make(name : String) -> Fox {
  Fox(name)
}

impl Animal for Fox with speak(_self) {
  "What does the fox say?"
}

test {
  let duck1 = Duck::make("duck1")
  let duck2 = Duck::make("duck2")
  let fox1 = Fox::make("fox1")
  let animals : Array[&Animal] = [duck1, duck2, fox1]
  debug_inspect(
    animals.map(fn(animal) { animal.speak() }),
    content=(
      #|["duck1: quack!", "duck2: quack!", "What does the fox say?"]
    ),
  )
}
```

并非所有特征都可以用于创建对象。“对象安全”特征的方法必须满足以下条件：

- `Self` 必须是方法的第一个参数
- 方法的类型中只能出现一个 `Self`（即第一个参数）

用户可以为特征对象定义新方法，就像为结构体和枚举定义新方法一样：

```moonbit
pub(open) trait Logger {
  write_string(Self, String) -> Unit
}

pub(open) trait CanLog {
  log(Self, &Logger) -> Unit
}

fn[Obj : CanLog] &Logger::write_object(self : &Logger, obj : Obj) -> Unit {
  obj.log(self)
}

/// 使用新的方法来简化代码
pub impl[A : CanLog, B : CanLog] CanLog for (A, B) with log(self, logger) {
  let (a, b) = self
  logger
  ..write_string("(")
  ..write_object(a)
  ..write_string(", ")
  ..write_object(b)
  .write_string(")")
}

```

### 内建特征

MoonBit 提供了以下有用的内建特征：

<!-- MANUAL CHECK https://github.com/moonbitlang/core/blob/80cf250d22a5d5eff4a2a1b9a6720026f2fe8e38/builtin/traits.mbt -->
```moonbit
trait Eq {
  op_equal(Self, Self) -> Bool
}

trait Compare : Eq {
  // `0` 代表相等，`-1` 代表小于，`1` 代表大于
  compare(Self, Self) -> Int
}

trait Hash {
  hash_combine(Self, Hasher) -> Unit // 待实现
  hash(Self) -> Int // 有默认实现
}

trait Show {
  output(Self, Logger) -> Unit // 待实现
  to_string(Self) -> String // 有默认实现
}

trait Default {
  default() -> Self
}

```

#### 派生内建特征

MoonBit 可以自动为一些内建特征派生实现：

```moonbit
struct T {
  a : Int
  b : Int
} derive(Eq, Compare, Debug, Default)

test {
  let t1 = T::default()
  let t2 = T::{ a: 1, b: 1 }
  debug_inspect(t1, content="{ a: 0, b: 0 }")
  debug_inspect(t2, content="{ a: 1, b: 1 }")
  assert_false(t1 == t2)
  assert_true(t1 < t2)
}
```

参见 [派生](derive.md) 了解有关派生特征的更多信息。

<!-- path: language/derive.md -->
## 派生内建特征

MoonBit 支持从类型定义中自动派生一些内建特征。

要派生特征 `T`，需要类型中使用的所有字段都实现了 `T`。例如，为结构体 `struct A { x: T1; y: T2 }` 派生 `Show` 需要 `T1: Show` 和 `T2: Show`。

### 相等和比较

`derive(Eq)` 和 `derive(Compare)` 将为测试相等性和比较生成相应的方法。字段按照它们的定义顺序进行比较。对于枚举，构造器的顺序按照定义的顺序升序。

```moonbit
struct DeriveEqCompare {
  x : Int
  y : Int
} derive(Eq, Compare)

test "derive eq_compare struct" {
  let p1 = DeriveEqCompare::{ x: 1, y: 2 }
  let p2 = DeriveEqCompare::{ x: 2, y: 1 }
  let p3 = DeriveEqCompare::{ x: 1, y: 2 }
  let p4 = DeriveEqCompare::{ x: 1, y: 3 }

  // Eq
  assert_eq(p1 == p2, false)
  assert_eq(p1 == p3, true)
  assert_eq(p1 == p4, false)
  assert_eq(p1 != p2, true)
  assert_eq(p1 != p3, false)
  assert_eq(p1 != p4, true)

  // Compare
  assert_eq(p1 < p2, true)
  assert_eq(p1 < p3, false)
  assert_eq(p1 < p4, true)
  assert_eq(p1 > p2, false)
  assert_eq(p1 > p3, false)
  assert_eq(p1 > p4, false)
  assert_eq(p1 <= p2, true)
  assert_eq(p1 >= p2, false)
}
```

```moonbit
enum DeriveEqCompareEnum {
  Case1(Int)
  Case2(label~ : String)
  Case3
} derive(Eq, Compare)

test "derive eq_compare enum" {
  let p1 = DeriveEqCompareEnum::Case1(42)
  let p2 = DeriveEqCompareEnum::Case1(43)
  let p3 = DeriveEqCompareEnum::Case1(42)
  let p4 = DeriveEqCompareEnum::Case2(label="hello")
  let p5 = DeriveEqCompareEnum::Case2(label="world")
  let p6 = DeriveEqCompareEnum::Case2(label="hello")
  let p7 = DeriveEqCompareEnum::Case3

  // Eq
  assert_eq(p1 == p2, false)
  assert_eq(p1 == p3, true)
  assert_eq(p1 == p4, false)
  assert_eq(p1 != p2, true)
  assert_eq(p1 != p3, false)
  assert_eq(p1 != p4, true)

  // Compare
  assert_eq(p1 < p2, true) // 42 < 43
  assert_eq(p1 < p3, false)
  assert_eq(p1 < p4, true) // Case1 < Case2
  assert_eq(p4 < p5, true)
  assert_eq(p4 < p6, false)
  assert_eq(p4 < p7, true) // Case2 < Case3
}
```

### Debug

`derive(Debug)` 会为类型生成结构化的调试实现。它可用于测试中的 `debug_inspect`，也可在格式化诊断信息时配合 `@debug.to_string` 使用。

```moonbit
struct DebugPoint {
  x : Int
  y : Int
} derive(Debug)

test "derive debug struct" {
  let point = DebugPoint::{ x: 1, y: 2 }
  debug_inspect(point, content="{ x: 1, y: 2 }")
}
```

枚举也可以派生 `Debug`：

```moonbit
enum DebugShape {
  Circle(radius~ : Int)
  Rect(width~ : Int, height~ : Int)
} derive(Debug)

test "derive debug enum" {
  let shape = DebugShape::Rect(width=3, height=4)
  debug_inspect(shape, content="Rect(width=3, height=4)")
}
```

### 默认值

`derive(Default)` 将生成一个返回类型的默认值的方法。

对于结构体，默认值是所有字段设置为它们的默认值的结构体。

```moonbit
struct DeriveDefault {
  x : Int
  y : String?
} derive(Default, Eq)

test "derive default struct" {
  let p = DeriveDefault::default()
  assert_true(p == DeriveDefault::{ x: 0, y: None })
}
```

对于枚举，默认值是唯一没有参数的构造器。

```moonbit
enum DeriveDefaultEnum {
  Case1(Int)
  Case2(label~ : String)
  Case3
} derive(Default, Eq)

test "derive default enum" {
  assert_true(DeriveDefaultEnum::default() == DeriveDefaultEnum::Case3)
}
```

没有构造器或有多个没有参数的构造器的枚举不能派生 `Default`。

<!-- MANUAL CHECK  should not compile -->
```moonbit
enum CannotDerive1 {
    Case1(String)
    Case2(Int)
} derive(Default) // cannot find a constant constructor as default

enum CannotDerive2 {
    Case1
    Case2
} derive(Default) // Case1 and Case2 are both candidates as default constructor
```

### 哈希值

`derive(Hash)` 将为类型生成一个哈希实现。这将允许类型在期望 `Hash` 实现的地方使用，例如 `HashMap` 和 `HashSet`。

```moonbit
struct DeriveHash {
  x : Int
  y : String?
} derive(Hash, Eq)

test "derive hash struct" {
  let hs = @hashset.HashSet([])
  hs.add(DeriveHash::{ x: 123, y: None })
  hs.add(DeriveHash::{ x: 123, y: None })
  @test.assert_eq(hs.length(), 1)
  hs.add(DeriveHash::{ x: 123, y: Some("456") })
  @test.assert_eq(hs.length(), 2)
}
```

### 任意值

`derive(Arbitrary)` 将生成给定类型的随机值。

### 从/到 Json

`derive(FromJson)` 和 `derive(ToJson)` 会自动派生可往返的方法实现，用于将类型序列化为 JSON 以及从 JSON 反序列化。该实现主要用于调试和以人类可读的格式存储类型。

```moonbit
struct JsonTest1 {
  x : Int
  y : Int
} derive(FromJson, ToJson, Eq)

enum JsonTest2 {
  A(x~ : Int)
  B(x~ : Int, y~ : Int)
} derive(FromJson(style="legacy"), ToJson(style="legacy"), Eq)

test "json basic" {
  let input = JsonTest1::{ x: 123, y: 456 }
  let expected : Json = { "x": 123, "y": 456 }
  @test.assert_eq(input.to_json(), expected)
  assert_true(@json.from_json(expected) == input)
  let input = JsonTest2::A(x=123)
  let expected : Json = { "$tag": "A", "x": 123 }
  @test.assert_eq(input.to_json(), expected)
  assert_true(@json.from_json(expected) == input)
}
```

这两个派生指令都接受一些参数来配置序列化和反序列化的确切行为。

##### WARNING
JSON 序列化参数的实际行为是不稳定的。

##### WARNING
JSON 派生参数仅用于粗粒度控制派生格式。如果你需要精确控制类型的布局，请考虑 **直接实现这两个特征**。

我们最近弃用了大量高级布局调整参数。对于这种用法和将来对它们的使用，请手动实现这些特征。这些参数包括：`repr`、`case_repr`、`default`、`rename_all` 等。

```moonbit
struct JsonTest3 {
  x : Int
  y : Int
} derive (
  FromJson(fields(x(rename="renamedX"))),
  ToJson(fields(x(rename="renamedX"))),
  Eq,
)

enum JsonTest4 {
  A(x~ : Int)
  B(x~ : Int, y~ : Int)
} derive(FromJson, ToJson, Eq)

test "json args" {
  let input = JsonTest3::{ x: 123, y: 456 }
  let expected : Json = { "renamedX": 123, "y": 456 }
  @test.assert_eq(input.to_json(), expected)
  assert_true(@json.from_json(expected) == input)
  let input = JsonTest4::A(x=123)
  let expected : Json = ["A", { "x": 123 }]
  @test.assert_eq(input.to_json(), expected)
  assert_true(@json.from_json(expected) == input)
}
```

#### 枚举样式

目前有两种枚举序列化样式：`legacy` 和 `flat`，用户必须使用 `style` 参数选择其中一种。

```moonbit
enum E {
  One
  Uniform(Int)
  Axes(x~: Int, y~: Int)
}
```

使用 `derive(ToJson(style="legacy"))`，枚举格式化为：

```default
E::One              => { "$tag": "One" }
E::Uniform(2)       => { "$tag": "Uniform", "0": 2 }
E::Axes(x=-1, y=1)  => { "$tag": "Axes", "x": -1, "y": 1 }
```

使用 `derive(ToJson(style="flat"))`，枚举格式化为：

```default
E::One              => "One"
E::Uniform(2)       => [ "Uniform", 2 ]
E::Axes(x=-1, y=1)  => [ "Axes", -1, 1 ]
```

##### 派生 `Option`

一个特例是内建类型 `Option[T]`。理想情况下，它会被解释为 `T | undefined`，但问题是对于 `Option[Option[T]]` 来说，无法区分 `Some(None)` 和 `None`。

因此，当且仅当它是结构体的直接字段时，它被解释为 `T | undefined`，否则解释为 `[T] | null`：

```moonbit
struct A {
  x : Int?
  y : Int??
  z : (Int?, Int??)
} derive(ToJson)

test {
  json_inspect({ x: None, y: None, z: (None, None) }, content={
    "z": [null, null],
  })
  json_inspect({ x: Some(1), y: Some(None), z: (Some(1), Some(None)) }, content={
    "x": 1,
    "y": null,
    "z": [[1], [null]],
  })
  json_inspect({ x: Some(1), y: Some(Some(1)), z: (Some(1), Some(Some(1))) }, content={
    "x": 1,
    "y": [1],
    "z": [[1], [[1]]],
  })
}
```

#### 容器参数

- `rename_fields` 和 `rename_cases`（仅适用于枚举）分别批量重命名字段（对于枚举和结构体）和枚举构造器为给定格式。可用的参数有：
  - `lowercase`
  - `UPPERCASE`
  - `camelCase`
  - `PascalCase`
  - `snake_case`
  - `SCREAMING_SNAKE_CASE`
  - `kebab-case`
  - `SCREAMING-KEBAB-CASE`

  例如：`rename_fields = "PascalCase"` 用于名为 `my_long_field_name` 的字段将得到 `MyLongFieldName`。

  重命名假定字段的名称为 `snake_case`，结构体/枚举构造器的名称为 `PascalCase`。
- `cases(...)`（仅枚举）控制枚举构造器的布局。

  #### WARNING
  这可能在未来会被构造器属性替换。

  例如，对于一个枚举
  ```moonbit
  enum E {
    A(...)
    B(...)
  }
  ```

  您可以使用 `cases(A(...), B(...))` 控制每个构造器。

  有关详细信息，请参见下面的 [构造器参数]()。
- `fields(...)`（仅结构体）控制结构体字段的布局。

  #### WARNING
  这可能在未来会被字段属性替换。

  例如，对于一个结构体
  ```moonbit
  struct S {
    x: Int
    y: Int
  }
  ```

  您可以使用 `fields(x(...), y(...))` 控制每个字段。

  有关详细信息，请参见下面的 [字段参数]()。

#### 构造器参数

- `rename = "..."` 重命名此特定构造器，覆盖现有的容器范围重命名指令（如果有的话）。
- `fields(...)` 控制此构造器的负载布局。请注意，目前无法重命名位置字段。

  有关详细信息，请参见下面的 [字段参数]()。

#### 字段参数

- `rename = "..."` 重命名此特定字段，覆盖现有的容器范围重命名指令（如果有的话）。

<!-- path: language/error-handling.md -->
## 错误处理

错误处理一直是我们语言设计的核心。接下来我们将解释 MoonBit 中的错误处理。我们假设您对 MoonBit 有一些了解，如果没有，请查看 [MoonBit 之旅](../tutorial/tour.md)。

### 错误类型

在 MoonBit 中，所有的错误类型都可以用 `Error`，一个通用的错误类型，来表示。

但是，`Error` 不能直接构造。必须定义一个具体的错误类型，形式如下：

```moonbit
suberror E1 { E1(Int) }  // 错误类型 E1 具有一个构造器 E1，并带有一个 Int 负载
suberror E2              // 错误类型 E2 具有一个没有负载的构造器 E2
suberror E3 {            // 错误类型 E3 类似于普通的枚举类型，有三个构造器
  A
  B(Int, x~ : String)
  C(mut x~ : String, Char, y~ : Bool)
}

```

##### WARNING
旧的 `suberror A B` 语法已弃用。请改用 `suberror A { A(B) }`。

错误类型可以自动提升为 `Error` 类型，并且可以模式匹配回去：

```moonbit
suberror CustomError { CustomError(UInt) }

test {
  let e : Error = CustomError(42)
  guard e is CustomError(m)
  assert_eq(m, 42)
}
```

由于 `Error` 类型可以包含多个错误类型，对 `Error` 类型进行模式匹配必须使用通配符 `_` 来匹配所有错误类型。例如：

```moonbit
fn f(e : Error) -> Unit {
  match e {
    E2 => println("E2")
    A => println("A")
    B(i, x~) => println("B(\{i}, \{x})")
    _ => println("unknown error")
  }
}
```

`Error` 通常用于不需要具体错误类型的情况，或者简单地用来捕获所有的子错误。

#### Failure

一个内置的错误类型是 `Failure`。

`fail` 是一个便利的函数，它只是一个带有预定义输出模板的构造函数，用于显示错误和源位置。在实践中，`fail!` 总是比 `Failure`更常用。

<!-- MANUAL CHECK -->
```moonbit
##callsite(autofill(loc))
pub fn[T] fail(msg : String, loc~ : SourceLoc) -> T raise Failure {
  raise Failure("FAILED: \{loc} \{msg}")
}
```

### 抛出错误

关键字 `raise` 被用来中断函数执行并返回一个错误。

函数的类型签名可以用 `raise` 和一个错误类型来表明函数可能抛出一个错误。例如，以下函数 `div` 可能返回一个类型为 `DivError` 的错误：

```moonbit
suberror DivError { DivError(String) } derive(Debug)

fn div(x : Int, y : Int) -> Int raise DivError {
  if y == 0 {
    raise DivError("division by zero")
  }
  x / y
}
```

`Error` 类型可以在具体的错误类型不重要时使用。为了方便起见，您可以在 `raise` 之后省略错误类型，以表示使用了 `Error` 类型。例如，以下函数签名是等价的：

```moonbit
fn f() -> Unit raise {
  ...
}

fn g() -> Unit raise Error {
  let h : () -> Unit raise = fn() raise { fail("fail") }
  ...
}
```

对于在错误类型上是泛型的函数，您可以使用 `Error` 约束来实现。例如：

```moonbit
// Result::unwrap_or_error
fn[T, E : Error] unwrap_or_error(result : Result[T, E]) -> T raise E {
  match result {
    Ok(x) => x
    Err(e) => raise e
  }
}
```

对于不抛出错误的函数，您可以在签名中添加 `noraise`。例如：

```moonbit
fn add(a : Int, b : Int) -> Int noraise {
  a + b
}
```

#### 错误多态

一个高阶函数在接受另一个函数作为参数时，另一个函数可能会抛出错误，也可能不会抛出错误，这反过来又影响了这个高阶函数的行为。

一个典型的例子是 `Array` 的 `map` 函数：

```moonbit
fn[T] map(array : Array[T], f : (T) -> T raise) -> Array[T] raise {
  let mut res = []
  for x in array {
    res.push(f(x))
  }
  res
}
```

然而，这样写会使得 `map` 函数总是有可能抛出错误，这并不是我们想要的。

因此，引入了错误多态。您可以使用 `raise?` 来表示可能会抛出错误，也可能不会抛出错误。

```moonbit
fn[T] map_with_polymorphism(
  array : Array[T],
  f : (T) -> T raise?
) -> Array[T] raise? {
  let mut res = []
  for x in array {
    res.push(f(x))
  }
  res
}

fn[T] map_without_error(
  array : Array[T],
  f : (T) -> T noraise,
) -> Array[T] noraise {
  map_with_polymorphism(array, f)
}

fn[T] map_with_error(array : Array[T], f : (T) -> T raise) -> Array[T] raise {
  map_with_polymorphism(array, f)
}
```

`map_with_polymorphism` 的签名将由实际参数推导而来。

### 处理错误

直接调用函数会在出现错误时直接重新抛出错误。例如：

```moonbit
fn div_reraise(x : Int, y : Int) -> Int raise DivError {
  div(x, y) // 如果 `div` 引发错误，则重新抛出错误
}

```

但是，你可能想要处理错误。

#### Try ... Catch

你可以使用 `try` 和 `catch` 捕获和处理错误，例如：

```moonbit
fn main {
  try div(42, 0) catch {
    DivError(s) => println(s)
  } noraise {
    v => println(v)
  }
}
```

```default
除零
```

这里，`try` 用于调用可能引发错误的函数，`catch` 用于匹配和处理捕获的错误。如果没有捕获到错误，`catch` 块将不会执行，而是执行 `noraise` 块。

如果不需要在没有捕获到错误时执行任何操作，则可以省略 `noraise` 块。例如：

```moonbit
println(div(42, 0)) catch {
  _ => println("Error")
}
```

当 `try` 的主体是一个简单表达式时，大括号，甚至 `try` 关键字，都可以省略。例如：

```moonbit
let a = div(42, 0) catch { _ => 0 }
println(a)
```

#### 转换为 Result

您还可以捕获潜在的错误，并将其转换为一等公民的 [`Result`](fundamentals.md#option-and-result) 类型：

```moonbit
test {
  let res : Result[Int, DivError] = Ok(div(6, 0) * div(6, 3)) catch {
    error => Err(error)
  }
  match res {
    Err(DivError(message)) => @test.assert_eq(message, "division by zero")
    Ok(_) => fail("expected division to fail")
  }
}
```

#### 出错时崩溃

如果发生意外错误，您也可以直接引发崩溃：

```moonbit
fn remainder(a : Int, b : Int) -> Int raise DivError {
  if b == 0 {
    raise DivError("division by zero")
  }
  let div = try! div(a, b)
  a - b * div
}
```

#### 错误推导

在 `try` 块中，可能引发多种不同类型的错误。当发生这种情况时，编译器将使用 `Error` 类型作为通用错误类型。因此，处理程序必须使用通配符 `_` 来确保捕获所有错误，并且通过 `e => raise e` 来抛出其他错误。例如：

```moonbit
fn f1() -> Unit raise E1 {
  ...
}

fn f2() -> Unit raise E2 {
  ...
}

try {
  f1()
  f2()
} catch {
  E1(_) => ...
  E2 => ...
  e => raise e
}
```

<!-- path: language/packages.md -->
## 使用包管理项目

在大规模开发项目时，项目通常需要分解为相互依赖的较小模块单元。更常见的是使用其他人的工作：最典型的是 [core](https://github.com/moonbitlang/core)，MoonBit 的标准库。

### 包和模块

In MoonBit, the most important unit for code organization is a package, which consists of a number of source code files and a single package configuration file (`moon.pkg`, or the legacy `moon.pkg.json` format).
A package can either be a `main` package, consisting a `main` function, or a package that serves as a library, identified by the [`is-main`](../toolchain/moon/package.md#is-main) field.

一个项目对应一个模块，由多个包和一个单独的 `moon.mod.json` 配置文件组成。

一个模块由 [`name`](../toolchain/moon/module.md#source-directory) 字段标识，通常由两部分组成，用 `/` 分隔：`user-name/project-name`。包由相对于源代码根目录的路径标识，该路径由 [`source`](../toolchain/moon/module.md#source-directory) 字段定义。完整标识符为 `user-name/project-name/path-to-pkg`。

When using things from another package, the dependency between modules should first be declared inside the `moon.mod.json` by the [`deps`](../toolchain/moon/module.md#dependency-management) field.
The dependency between packages should then be declared in the package file (`moon.pkg`, or legacy `moon.pkg.json`) by the [`import`](../toolchain/moon/package.md#import) field.
Most core packages follow the same rule: if you use `@json`, `@test`, or other
ordinary core aliases, add the corresponding `moonbitlang/core/...` package to
`import` to avoid `core_package_not_imported` warnings.

<a id="default-alias"></a>

包的**默认别名**是标识符中 `/` 分隔的最后一部分。可以使用 `@pkg_alias` 访问导入的实体，其中 `pkg_alias` 可以是完整标识符或默认别名。也可以使用 [`import`](../toolchain/moon/package.md#import) 字段定义自定义别名。

在 `moon.pkg` 中，自定义别名写法为：

```text
import {
  "moonbit-community/language/packages/pkgA",
  "moonbit-community/language/packages/pkgC" @c,
  "moonbitlang/core/builtin",
}
```

```moonbit
///|
pub fn add1(x : Int) -> Int {
  @moonbitlang/core/builtin.Add::add(0, @c.incr(@pkgA.incr(x)))
}
```

#### Prelude 与内建名称

如果省略 `@pkg.`，MoonBit 会在当前包和 prelude 中解析未限定名称。因此，本地定义的同名标识符会遮蔽 prelude 中的定义。

```moonbit
fn println(msg : String) -> String {
  "log: \{msg}"
}

///|
fn shadowed_println() -> String {
  println("hello")
}

///|
fn builtin_answer() -> Int {
  let answer : Int = 42
  answer
}
```

`prelude` is a special package: it is available by default, and names exposed
through it participate in normal unqualified name resolution without an
explicit `import`.

编译器内建项是另一类概念。像 `Int` 这样的类型是语言本身内建的，不是从任何包中导入进来的，因此不存在 `@builtin.Int`。`String`、`Bool` 和 `Unit` 等其他由编译器识别的名称也是如此。

#### 内部包

你可以定义只对某些包可用的内部包。

在 `a/b/c/internal/x/y/z` 中的代码只对包 `a/b/c` 和 `a/b/c/**` 可用。

#### 使用

你可以使用 `using` 语法导入在另一个包中定义的符号。

```moonbit
///|
pub using @pkgA {incr, trait Trait, type Type}
```

通过使用 `pub` 修饰符，它被视为重新导出。

<a id="access-control"></a>

### 访问控制

MoonBit 具有全面的访问控制系统，管理代码的哪些部分可以从其他包访问。此系统有助于维护封装、信息隐藏和清晰的 API 边界。可见性修饰符适用于函数、变量、类型和特征，允许对代码如何被他人使用进行细粒度控制。

#### 函数

默认情况下，所有函数定义和变量绑定对其他包是  *不可见* 的。可以在顶层 `let`/`fn` 前使用 `pub` 修饰符使其公开。

#### 别名

默认情况下，[函数别名](fundamentals.md#type-alias) 和 [方法别名](methods.md#alias-methods-as-functions) 遵循原始定义的可见性，而 [类型别名](fundamentals.md#type-alias)、[using]() 对其他包是  *不可见* 的。

可以在定义之前添加 `pub` 修饰符，或在注释中填写 `visibility` 字段。

#### 类型

MoonBit 中有四种不同的类型可见性：

- 私有类型，使用 `priv` 声明，对外部世界完全不可见
- 抽象类型：这是类型的默认可见性。

  只有抽象类型的名称在外部可见，该类型的内部表示是隐藏的。将抽象类型默认设置为隐藏是设计选择，旨在鼓励封装和信息隐藏。
- 使用 `const` 声明的常量

  只读类型，使用 `pub` 声明。只读类型的内部表示对外部可见，但用户只能从外部读取这些类型的值，不允许构造和修改
- 完全公开的类型，使用 `pub(all)` 声明。

  完全公开类型，使用 `pub(all)` 声明。外部世界可以自由构造、修改和读取这些类型的值

除了类型本身的可见性外，公开的结构体的字段可以用 `priv` 注释，这将对外部世界完全隐藏字段。请注意，具有私有字段的 `struct` 不能直接在外部构造，但可以使用函数式 `struct` 更新语法更新公开字段。

只读类型是一个非常有用的功能，受到 OCaml 中 [私有类型](https://ocaml.org/manual/5.3/privatetypes.html) 的启发。简而言之，`pub` 类型的值可以通过模式匹配和点语法解构，但不能在其他包中构造或修改。请注意，在定义 `pub` 类型的同一包中没有限制。

##### NOTE
在定义 `pub` 类型的同一包中没有任何限制。

<!-- MANUAL CHECK -->
```moonbit
// Package A
pub struct RO {
  field: Int
}
test {
  let r = { field: 4 }       // 可以
  let r = { ..r, field: 8 }  // 可以
}

// Package B
fn println(r : RO) -> Unit {
  println("{ field: ")
  println(r.field)  // 可以
  println(" }")
}
test {
  let r : RO = { field: 4 }  // 错误：不能创建公开只读类型 RO 的值！
  let r = { ..r, field: 8 }  // 错误：不能修改公开只读字段！
}

```

MoonBit 中的访问控制遵循一个原则，即公开的类型、函数或变量不能以私有类型定义。这是因为私有类型可能无法在使用公开的实体的所有地方访问。MoonBit 包含了健全性检查，以防止违反这一原则的用例发生。

<!-- MANUAL CHECK -->
```moonbit
pub(all) type T1
pub(all) type T2
priv type T3

pub(all) struct S {
  x: T1  // 可以
  y: T2  // 可以
  z: T3  // 错误：公开字段使用了私有类型 `T3`！
}

// 错误：公开函数使用了私有类型 `T3`！
pub fn f1(_x: T3) -> T1 { ... }
// 错误：公开函数的返回值使用了私有类型 `T3`！
pub fn f2(_x: T1) -> T3 { ... }
// 可以
pub fn f3(_x: T1) -> T1 { ... }

pub let a: T3 = { ... } // 错误：公开变量的类型是私有类型 `T3`！

```

#### 特征

特征有四种可见性，就像 `struct` 和 `enum`：私有、抽象、只读和完全公开。

- 私有类型，使用 `priv` 声明，对外部世界完全不可见
- 特征默认是抽象的。只有特征类型的名称对外部可见，特征的方法被隐藏
- 只读特征，使用 `pub trait` 声明，它们的方法可以从外部调用，但只有当前包可以为只读特征添加新实现
- 完全公开的特征，使用 `pub(open) trait` 声明，在外部包也可以增加实现，其方法可以自由使用

抽象特征和只读特征是封闭的，因为只有定义特征的包才能实现它们。在包外实现封闭（抽象或只读）特征会导致编译器错误。

##### 特征的实现

实现本身有独立的可见性，就像函数一样。除非实现是 `pub`，否则在当前包之外，类型不会被认为已经实现了特征。

为了使特征系统一致（即每个 `Type: Trait` 对都有全局唯一的实现），并防止第三方包意外地修改现有程序的行为，MoonBit 对谁可以定义方法/实现类型的特征采用了以下限制：

- **只有定义类型的包才能为其定义方法**。因此，不能为内建和外部类型定义新方法或覆盖旧方法。
  - 这条规则有一个例外：[本地方法](methods.md#local-method)。不过，本地方法永远是私有的，因此它们不会破坏 MoonBit 类型系统的一致性。
- **只有类型的包或特征的包才能定义实现**。例如，只有 `@pkg1` 和 `@pkg2` 允许编写 `impl @pkg1.Trait for @pkg2.Type`。

上述第二条规则允许通过定义新特征并实现它来为外部类型添加新功能。这使 MoonBit 的特征和方法系统灵活，同时享有良好的一致性属性。

##### WARNING
目前，空特征会自动实现。

以下是抽象特征的示例：

<!-- MANUAL CHECK -->
```moonbit
trait Number {
 op_add(Self, Self) -> Self
 op_sub(Self, Self) -> Self
}

fn[N : Number] add(x : N, y: N) -> N {
  Number::op_add(x, y)
}

fn[N : Number] sub(x : N, y: N) -> N {
  Number::op_sub(x, y)
}

impl Number for Int with op_add(x, y) { x + y }
impl Number for Int with op_sub(x, y) { x - y }

impl Number for Double with op_add(x, y) { x + y }
impl Number for Double with op_sub(x, y) { x - y }
```

从包外，用户只能看到以下内容：

```moonbit
trait Number

fn[N : Number] op_add(x : N, y : N) -> N
fn[N : Number] op_sub(x : N, y : N) -> N

impl Number for Int
impl Number for Double
```

`Number` 的作者可以利用只有 `Int` 和 `Double` 可以实现 `Number` 这一事实，因为在外部不允许新的实现。

### 虚拟包

##### WARNING
虚拟包是一个实验性功能。可能存在错误和未定义的行为。

你可以定义虚拟包，它作为一个接口。它们可以在构建时被特定的实现替换。目前，虚拟包只能包含普通函数。

虚拟包在保持代码不变的情况下，可以在不同的实现之间进行切换。

#### 定义一个虚拟包

你需要声明它是一个虚拟包，并在 MoonBit 接口文件中定义其接口。

Within `moon.pkg`, you will need to add field [`virtual`](../toolchain/moon/package.md#declarations) :

```text
options(
  "virtual": { "has-default": true },
)
```

The `has-default` 表示虚拟包是否有默认实现。

在包内，你需要添加一个接口文件 `pkg.mbti`：

```moonbit
package "moonbit-community/language/packages/virtual"

fn log(String) -> Unit
```

接口文件的第一行需要是 `package "full-package-name"`。接下来是声明。[访问控制]() 的 `pub` 关键字和函数参数名称应该省略。

#### 实现虚拟包

一个虚拟包可以有一个默认实现。通过将 [`virtual.has-default`](../toolchain/moon/package.md#declarations) 设置为 `true`，你可以在同一包内照常实现代码。

```moonbit
///|
pub fn log(s : String) -> Unit {
  println(s)
}
```

虚拟包也可以由第三方实现。通过将 [`implements`](../toolchain/moon/package.md#implementations) 定义为目标包的完整名称，编译器可以警告你关于缺失的实现或不匹配的实现。

```text
options(
  implement: "moonbit-community/language/packages/virtual",
)
```

```moonbit
///|
pub fn log(string : String) -> Unit {
  ignore(string)
}
```

#### 使用虚拟包

要使用虚拟包，与其他包一样：在你想要使用它的包中定义 [`import`](../toolchain/moon/package.md#import) 字段。

#### 覆盖虚拟包

如果虚拟包有一个默认实现，并且是你想要的，则不需要额外的配置。

否则，你可以通过提供一个实现数组来定义 [`overrides`](../toolchain/moon/package.md#overriding-implementations) 字段，指定你想要使用的实现。

```text
import {
  "moonbit-community/language/packages/virtual",
}

options(
  "is-main": true,
  overrides: [ "moonbit-community/language/packages/implement" ],
)
```

在使用实体时，你应该引用虚拟包。

```moonbit
///|
fn main {
  @virtual.log("Hello")
}
```

<!-- path: language/tests.md -->
## 编写测试

测试对于提高程序的质量和可维护性很重要。它们验证程序的行为，也作为规范，以避免随时间的推移出现退步。

MoonBit 提供测试支持，使编写测试更加简单和容易。

### 测试块

MoonBit 提供测试代码块，用于编写内联测试用例。例如：

```moonbit
test "test_name" {
  assert_eq(1 + 1, 2)
  assert_eq(2 + 2, 4)
  debug_inspect([1, 2, 3], content="[1, 2, 3]")
}
```

测试代码块本质上是一个返回 `Unit` 抛出 [`Error`](error-handling.md#error-types) 的函数，或者说返回值签名为 `Unit!Error`。它在执行 `moon test` 期间被调用，并通过构建系统输出测试报告。`assert_eq` 函数来自标准库；如果断言失败，它会打印错误消息并终止测试。字符串 `"test_name"` 用于标识测试用例，是可选的。

如果测试名称以 `"panic"` 开头，表示测试的预期行为是触发 panic，只有在触发 panic 时测试才会通过。例如：

```moonbit
test "panic_test" {
  let _ : Int = Option::None.unwrap()
}
```

### 快照测试

在指定预期值时，编写测试可能会很繁琐。因此，MoonBit 提供了三种快照测试。所有这些都可以使用 `moon test --update` 自动插入或更新。

#### 快照 `Show`

我们可以使用 `inspect(x, content="x")` 来检查实现了 `Show` 特征的任何内容。正如我们之前提到的，`Show` 是一个可以派生的内建特征，提供了 `to_string`来打印数据结构的内容。标记参数 `content` 可以省略，因为 `moon test --update` 会自动插入：

```moonbit
struct X {
  x : Int
} derive(Debug)

test "show snapshot test" {
  debug_inspect({ x: 10 }, content="{ x: 10 }")
}
```

#### 快照 `JSON`

派生的 `Show` 特征的问题是它不能对输出进行格式化，导致输出非常长。

解决方案是使用 `@json.inspect(x, content=x)`。其好处是生成的内容是一个 JSON 结构，在格式化后更易读。

```moonbit
enum Rec {
  End
  Really_long_name_that_is_difficult_to_read(Rec)
} derive(Debug, ToJson)

test "json snapshot test" {
  let r = Really_long_name_that_is_difficult_to_read(
    Really_long_name_that_is_difficult_to_read(
      Really_long_name_that_is_difficult_to_read(End),
    ),
  )
  debug_inspect(
    r,
    content="Really_long_name_that_is_difficult_to_read(Really_long_name_that_is_difficult_to_read(Really_long_name_that_is_difficult_to_read(End)))",
  )
  json_inspect(r, content=[
    "Really_long_name_that_is_difficult_to_read",
    [
      "Really_long_name_that_is_difficult_to_read",
      ["Really_long_name_that_is_difficult_to_read", "End"],
    ],
  ])
}
```

也可以实现自定义 `ToJson` 来保留必要的信息。

#### 快照任何内容

有时我们不仅要记录一个数据结构，还要记录整个过程的输出。

可以使用完整的快照测试使用 `@test.T::write` 和 `@test.T::writeln` 记录任何内容：

```moonbit
test "record anything" (t : @test.Test) {
  t.write("Hello, world!")
  t.writeln(" And hello, MoonBit!")
  t.snapshot(filename="record_anything.txt")
}
```

这将在该包的 `__snapshot__` 下创建一个具有给定文件名的文件：

```default
Hello, world! And hello, MoonBit!
```

这也可以用于测试应用程序的输出，无论是创建图像、视频还是一些自定义数据。

请注意，`@test.T::snapshot` 应该在测试块的末尾使用，因为它总是引发异常。

### 黑盒测试和白盒测试

在开发库时，验证用户是否可以正确使用它是很重要的。例如，可能会忘记使类型或函数公开。这就是为什么 MoonBit 提供了黑盒测试，允许开发人员了解其他人的感受。

- 能够访问包中所有成员的测试称为白盒测试，因为我们可以看到一切。这样的测试可以内联定义，也可以在文件中定义，文件名以 `_wbtest.mbt` 结尾。
- 只能访问包中公开成员的测试称为黑盒测试。这样的测试需要在文件中定义，文件名以 `_test.mbt` 结尾。

The WhiteBox test files (`_wbtest.mbt`) imports the packages defined in the `import` and `wbtest-import` sections of the package configuration (`moon.pkg`, or legacy `moon.pkg.json`).

The BlackBox test files (`_test.mbt`) imports the current package and the packages defined in the `import` and `test-import` sections of the package configuration (`moon.pkg`, or legacy `moon.pkg.json`).

<!-- path: language/benchmarks.md -->
## 编写基准测试

基准是一种衡量代码性能的方法。它们可以用于比较不同的实现质量，或跟踪随时间变化的性能。

### 使用测试块进行基准测试

对一个函数进行基准测试的最简单的方法是使用带有 `@bench.T` 参数的测试块。它有一个方法 `@bench.T::bench`，该方法接受一个类型为 `() -> Unit` 的函数，并自动确定合适的迭代次数然后运行该函数多次。测量和统计分析将在 `moon` 中进行，并在控制台输出中显示。

```moonbit
fn fib(n : Int) -> Int {
  if n < 2 {
    return n
  }
  return fib(n - 1) + fib(n - 2)
}

test (b : @bench.T) {
  b.bench(fn() { b.keep(fib(20)) })
}
```

输出如下所示（仅供参考）：

```default
time (mean ± σ)         range (min … max) 
  21.67 µs ±   0.54 µs    21.28 µs …  23.14 µs  in 10 ×   4619 runs
```

该函数执行了 `10 × 4619` 次。第二个数字是由基准工具自动确定的。它会增加迭代次数，直到测量时间足够长，以获得准确的计时。第一个数字可以通过向 `@bench.T::bench` 传递命名参数 `count` 来调整。

```moonbit
test (b : @bench.T) {
  b.bench(fn() { b.keep(fib(20)) }, count=20)
}
```

`@bench.T::keep` 是一个重要的辅助函数，它可以防止你的计算被优化掉并完全跳过。如果你正在基准测试一个纯函数，请确保使用这个函数以避免潜在的优化。然而，编译器仍然有可能提前计算并将计算替换为常量。

### 批量基准测试

基准测试的一个常见场景是比较同一函数的两个或多个实现。在这种情况下，你可能想要在一个块中批量测试它们，以便于比较。`@bench.T::bench` 方法的 `name` 参数可以用来标识测试块内的基准测试。

```moonbit
fn fast_fib(n : Int) -> Int {
  if n < 2 {
    return n
  } else {
    let mut a = 0
    let mut b = 1
    for i = 2; i <= n; i = i + 1 {
      let t = a + b
      a = b
      b = t
    }
    b
  }
}

test (b : @bench.T) {
  b.bench(name="naive_fib", fn() { b.keep(fib(20)) })
  b.bench(name="fast_fib", fn() { b.keep(fast_fib(20)) })
}
```

现在你可以通过查看输出，来评估哪个实现更快：

```default
name      time (mean ± σ)         range (min … max) 
naive_fib   21.01 µs ±   0.21 µs    20.76 µs …  21.32 µs  in 10 ×   4632 runs
fast_fib     0.02 µs ±   0.00 µs     0.02 µs …   0.02 µs  in 10 × 100000 runs
```

### 基准统计的原始数据

有时用户可能希望获取原始基准统计数据以进行进一步的分析。有一个函数 `@bench.single_bench` 返回一个抽象的 `Summary` 类型，用户可以将其序列化为 JSON 格式。我们不保证 `Summary` 类型内部字段的稳定性。

在使用 `@bench.single_bench` 时，用户必须确保计算没有被优化掉。此时没有独立的 `keep` 函数可用；之前介绍过的 `keep` 是 `@bench.T` 的一个方法。

```moonbit
fn collect_bench() -> Unit {
  let mut saved = 0
  let summary : @bench.Summary = @bench.single_bench(name="fib", fn() {
    saved = fib(20)
  })
  println(saved)
  println(summary.to_json().stringify())
}

```

输出可能如下所示：

```json
6765
{
    "name": "fib",
    "sum": 217.22039973878972,
    "min": 21.62009230518067,
    "max": 21.87286402916848,
    "mean": 21.72203997387897,
    "median": 21.70412048323901,
    "var": 0.007197724461032505,
    "std_dev": 0.08483940394081341,
    "std_dev_pct": 0.39056830777787843,
    "median_abs_dev": 0.08189815918589166,
    "median_abs_dev_pct": 0.3773392211360855,
    "quartiles": [
        21.669052078798433,
        21.70412048323901,
        21.76141434479756
    ],
    "iqr": 0.09236226599912811,
    "batch_size": 4594,
    "runs": 10
}
```

时间单位均为微秒。

<!-- path: language/docs.md -->
## 注释与文档

### 注释

在代码内部使用 `//` 编写普通注释：

```moonbit
// Explain why this branch exists.
let retries = 3
```

你也会经常在顶层代码块开头看到 `///|`。它是一条空的文档注释行，用来显式分隔顶层条目。这对于当前的文档工具链很重要，也能为未来的缓存和其他工具用途保留清晰的顶层块边界。实际使用中，`///|` 不仅适用于文档源码，在普通的 MoonBit 代码里也很有用。

### 文档注释

文档注释使用 `///`，逐行写在 `fn`、`let`、`enum`、`struct`、`type` 等顶层条目前面。文档注释内容使用 Markdown 编写。

```moonbit
/// 返回一个新数组，其中元素被反转。
fn[T] reverse(xs : Array[T]) -> Array[T] {
  ...
}

```

文档注释中的 Markdown 代码块如果标记为 `mbt check`，就会被视作文档测试；`moon check` 和 `moon test` 会自动检查并运行这些测试，从而保证文档注释中的示例始终是最新的。需要测试块时，请将代码包在 `test { .. }` 中：

```moonbit
/// 将一个整数加一，
///
/// 示例：
/// ```mbt check
/// test {
///   inspect(incr(41), content="42")
/// }
/// ```
pub fn incr(x : Int) -> Int {
  x + 1
}

```

如果想要阻止一段代码被当作文档测试，只需要在它的 markdown 代码块上标记一个 `mbt check` 以外的语言名字：

```moonbit
/// `c_incr(x)` 等价于下面的 C 代码：
/// ```c
/// x++
/// ```
pub fn c_incr(x : Ref[Int]) -> Int {
  let old = x.val
  x.val += 1
  old
}

```

目前，文档测试都是 [黑盒测试](tests.md#blackbox-tests-and-whitebox-tests)。所以私有定义目前不能有文档测试。

### 文学化 `.mbt.md` 文件

MoonBit 也支持以 `.mbt.md` 结尾的文学化 Markdown 文件。这些文件既可以作为包内的可检查文档，也可以作为 `moon check` 和 `moon test` 的独立单文件输入。

对于独立文件，运行：

```bash
moon check README.mbt.md
moon test README.mbt.md
```

在项目内部，请继续使用包级别的 `moon check` 和 `moon test` 命令。

代码块的语言标记决定了每个代码块会如何处理：

- `mbt`：会编译其中的 MoonBit 代码，但不会创建测试入口。
- `mbt check`：MoonBit 文档测试代码。需要断言时，请在代码块内部使用 `test { .. }` 或 `async test`。
- `mbt nocheck`：展示 MoonBit 代码，但不编译也不测试。
- `moonbit`：普通的文档展示代码块，不会被编译或测试。

例如：

```markdown
```mbt nocheck
///|
fn helper() -> Int {
  42
}
```

```mbt check
///|
test "forty two" {
  inspect(40 + 2, content="42")
}
```

```mbt nocheck
///|
fn native_only() -> Unit {
  ...
}
```
```

独立的 `.mbt.md` 文件也可以声明 front matter 来指定导入或目标后端：

```markdown
---
moonbit:
  import:
    - path: moonbitlang/core/ref
      alias: ref
  backend:
    native
---

```mbt check
fn answer() -> Int {
  let cell : @ref.Ref[Int] = { val: 41 }
  cell.val + 1
}

///|
test "answer" {
  inspect(answer(), content="42")
}
```
```

当你想显式指定该文件可以直接导入哪些包时，使用 `moonbit.import`。当你只想声明模块依赖并让 Moon 自动合成导入时，使用 `moonbit.deps`。

<!-- path: language/attributes.md -->
## 属性

Attributes are annotations placed before structures in source code. They take the form `#attribute(...)`.
An attribute occupies the entire line, and newlines are not allowed within it.
Attributes do not normally affect the meaning of programs. Unused attributes will be reported as warnings.

属性的语法如下所示：

```text
attribute ::= '#' attribute-name
            | '#' attribute-name '(' attribute-arguments ')'

attribute-name ::= LIDENT | LIDENT '.' LIDENT

attribute-arguments ::= attribute-argument (',' attribute-argument )*

attribute-argument ::= expr | LIDENT '=' expr

expr ::= LIDENT | UIDENT | STRING | 'true' | 'false'
       | LIDENT '.' LIDENT
       | LIDENT '(' attribute-arguments ')'
       | LIDENT '.' LIDENT '(' attribute-arguments ')'
```

属性有两种类别：内建属性和用户自定义属性。例如：

```default
##deprecated("message")
##custom.attribute(key="value", flag=true)
```

第一个属性是一个内建的属性，在属性名中，它没有命名空间前缀。内建属性会被 MoonBit 编译器检查，并且有特殊的含义。

第二个属性是用户自定义属性，它的名字中包含命名空间前缀。用户自定义属性会被编译器忽略，通过解析源码，它能够被外部的工具使用。

##### NOTE
MoonBit 在设计上有意不支持运行时反射。运行时反射容易被滥用，使得工具链（例如编译器）在编译时不可能捕获到错误，让代码的维护变得更困难。它也会对性能优化产生负面影响。

我们更倾向于使用编译时的代码生成，维持静态类型分析和性能上的优势（代码生成也需要节约地使用，避免不必要的复杂性）。

### `deprecated` 属性

The `#deprecated` attribute is used to mark an API as deprecated. MoonBit emits
a warning when the deprecated API is used, and if the API is listed in completion,
it will be shown with a strikethrough style. For example:

```moonbit
##deprecated
pub fn foo() -> Unit {
  ...
}

##deprecated("Use Bar2 instead")
pub(all) enum Bar {
  Ctor1
  Ctor2
}
```

`#deprecated` 属性能够在如下上下文中使用：

- 顶层的值声明（包括 `fn`，`let` 和 `const`）
- 顶层的类型声明（包括 `type`，`struct` 和 `enum`）
- 特征声明
- 特征的默认实现

常见形式包括：

- `#deprecated`

  将这个项目标记成弃用的，并使用默认的警告信息。
- `#deprecated("使用 new_function 代替")`

  将 API 标记为弃用，并自定义一个警告信息。每当这个弃用的 API 被使用时，自定义信息将会显示在警告中。
- `#deprecated("使用 new_function 代替", skip_current_package=true)`

  将 API 标记为弃用的同时自定义警告信息，但是当 API 在同一个包内使用时不触发警告。
- `#deprecated(skip_current_package=true)`

  Marks the item as deprecated with a default warning message, but skips emitting warnings within the same package. When both a message and `skip_current_package` are present, either argument order is accepted.

### Alert Attribute

`#alert` 属性会为 API 附加一个类别和一条消息。当代码使用该 API 时，MoonBit 会发出 alert 警告。

```moonbit
##alert(unsafe, "This function is unsafe.")
fn[A] alert_unsafe_get(arr : Array[A], index : Int) -> A {
  arr[index]
}
```

第一个参数是 alert 类别，第二个参数是展示给用户的消息。该警告可以通过 `alert`、`alert_unsafe` 等警告名称配置。

For more detail, see [alert warning](../toolchain/moon/package.md#alert-warning).

### `alias` 属性

`alias` 属性用于重载一个和索引操作相关的运算符，或者为一个顶层函数与变量创建别名。它有两种形式：

- `#alias("op")`: `op` 是下面其中一个字符串，表示对应的索引操作：
  - `_[_]`：数组索引操作符
  - `_[_]=_`：数组索引赋值操作符
  - `_[_:_]`：op_as_view 操作符
- `#alias(id)`: 其中 `id` 是一个代表别名的标识符

所有形式都支持额外的参数：

- `visibility="modifier"`

  一个带标签的参数，用于改变别名的可见性。`modifier` 可以是 `pub` 或者 `priv`，如果没有声明，别名会和原始的函数或者变量有一样的可见性。
- `deprecated` 或者 `deprecated="message"`

  将别名标记为弃用的。如果提供了 `message` 信息，当别名被使用时，信息会被一起显示在警告中。

To graceful migration from old API to new API, you can rename the old API directly, and create an alias with the old name, mark it as deprecated. For
example:

```moonbit
##alias(old_name, deprecated)
fn new_name() -> Unit {
  ()
}
```

### `label_migration` 属性

The `#label_migration` attribute is used to help you safely evolve your API
by warning users during the transition period.

它有如下三种形式：

- `#label_migration(id, fill=true, msg="message")`

  The `fill` argument is used when you want to refactor an optional parameter.
  You can use `fill=true` when you want to eventually make an optional
  parameter required. You can use `fill=false` when you want to eventually
  remove an optional parameter.

  `msg`参数是一个字符串，用于提供额外的信息，告诉使用者关于可见性的变更。
  ```moonbit
  #label_migration(x, fill=true)
  #label_migration(y, fill=false)
  fn label_migration_fill(x? : Int = 0, y? : Int = 1) -> Int {
    x + y
  }
  ```
- `#label_migration(id, allow_positional=true, msg="message")`

  The `allow_positional` argument is used when you want a labelled parameter to be
  used without its label being provided. When the parameter is used positionally
  (without a label), the compiler reports a warning. This is useful when you want to change a positional parameter
  to a labelled parameter without breaking the downstream code.

  `msg`参数是一个字符串，用于提供额外的信息，告诉使用者关于可见性的变更。
  ```moonbit
  #label_migration(x, allow_positional=true)
  fn label_migration_allow_positional(x~ : Int) -> Int {
    x
  }
  ```
- `#label_migration(id, alias=new_id, msg="message")`

  The alias argument allows you to provide an alternative name to a labelled
  parameter. This is useful when renaming a parameter to maintain backward
  compatibility. If a warning message is provided, the compiler warns when
  using the alias; otherwise, the alias can be used without warnings.

  `msg`参数是一个字符串，用于提供额外的信息，告诉使用者关于可见性的变更。
  ```moonbit
  #label_migration(x, alias=xx)
  #label_migration(x, alias=y, msg="warning")
  fn label_migration_alias(x~ : Int) -> Int {
    x
  }
  ```

### `visibility` 属性

##### NOTE
这里的文档不涉及访问控制。关于如何使用 `pub`、`pub(all)` 和 `priv`，见[访问控制](packages.md#id1)。

The `#visibility` attribute is similar to the `#deprecated` attribute, but it is used to hint that a type will change its visibility in the future.
For outside usages, if the usage will be invalidated by the visibility change in future, a warning will be emitted.

```moonbit
// in @util package
##visibility(change_to="readonly", "Point will be readonly in the future.")
pub(all) struct Point {
  x : Int
  y : Int
}

##visibility(change_to="abstract", "Use new_text and new_binary instead.")
pub(all) enum Resource {
  Text(String)
  Binary(Bytes)
}

pub fn new_text(str : String) -> Resource {
  ...
}

pub fn new_binary(bytes : Bytes) -> Resource {
  ...
}

// in another package
fn main {
  let p = Point::{ x: 1, y: 2 } // warning
  let { x, y } = p // ok
  println(p.x) // ok
  match Resource::Text("") { // warning
    Text(s) => ... // waning
    Binary(b) => ... // warning
  }
}

```

The `#visibility` attribute takes a required `change_to` argument and an
optional `message` argument.

- `change_to`参数是一个表示该类型新的可见性的字符串，它可以是`"abstract"`或者`"readonly"`

  | `"change_to"`的值   | 将要禁止的使用                                  |
  |-------------------|------------------------------------------|
  | `"readonly"`      | 创建该类型的实例，或者变更实例的字段的值。                    |
  | `"abstract"`      | 创建该类型的实例、变更实例的字段的值、对它的实例进行模式匹配或者访问实例的字段。 |
- The optional `message` argument is a string that provides additional information about the visibility change.

### `internal` 属性

The `#internal` attribute is used to mark a function, type, or trait as internal.
Any usage of the internal function or type in other modules will emit an alert warning.

```moonbit
##internal(unsafe, "This is an unsafe function")
fn[A] internal_unsafe_get(arr : Array[A], index : Int) -> A {
  arr[index]
}
```

The internal attribute takes a required `category` argument and an optional
`message` argument. `category` is a identifier that indicates the category of
the alert, and `message` is a string that provides additional message for the
alert.

The alert warnings can be turn off by setting the `warn-list` in `moon.pkg`.
For more detail, see [alert warning](../toolchain/moon/package.md#alert-warning).

### Doc Hidden Attribute

`#doc(hidden)` 属性会在生成的文档中隐藏 API。

```moonbit
##doc(hidden)
pub fn hidden_helper() -> Unit {
  ()
}
```

它适用于必须继续对代码可用、但不应作为文档化 API 表面展示的公开声明。

### `warnings` 属性

The `#warnings` attribute is used to configure warning settings for a specific
top-level declaration. It can enable, disable or treat an enabled warning as error
for specific warnings in that declaration.

The argument is a string that specifies the warning list. It can contain multiple
warning names, each prefixed with a sign:

```moonbit
##warnings("-unused_value")
fn warnings_example() -> Unit {
  let x = 42
}
```

这些前缀有以下含义：

- `+warning_name`: 启用这个警告
- `-warning_name`: 关闭这个警告
- `@warning_name`: 如果这个警告已经启用，转换为错误

目前这个属性只对部分特定的警告工作。

To learn more about warning names, see [warning list](../toolchain/moon/package.md#warnings-list).

### Must Implement One 属性

`#must_implement_one` 属性用于 trait，要求每个实现都显式定义至少一个方法，而不是只依赖默认方法实现。

不带参数时，必须显式实现该 trait 的至少一个方法：

```moonbit
##must_implement_one
pub(open) trait RequireAnyMethod {
  f(Self) -> Unit = _
  g(Self) -> Unit = _
}

impl RequireAnyMethod with f(_) {}

impl RequireAnyMethod with g(_) {}

type AnyImpl

impl RequireAnyMethod for AnyImpl with f(_) {}
```

带方法名时，必须显式实现列出的方法中的至少一个：

```moonbit
##must_implement_one(f, g)
pub(open) trait RequireSelectedMethod {
  f(Self) -> Unit = _
  g(Self) -> Unit = _
  h(Self) -> Unit = _
}

impl RequireSelectedMethod with f(_) {}

impl RequireSelectedMethod with g(_) {}

impl RequireSelectedMethod with h(_) {}

type SelectedImpl

impl RequireSelectedMethod for SelectedImpl with g(_) {}
```

同一个 trait 上可以使用多个 `#must_implement_one` 属性，以要求显式实现多个方法组中的方法。

### Inline Attribute

`#inline` 属性是函数的优化提示。它会请求编译器尽可能内联该函数：

```moonbit
##inline
fn add_one(x : Int) -> Int {
  x + 1
}
```

使用 `#inline(never)` 可以请求编译器不要内联某个函数：

```moonbit
##inline(never)
fn keep_stack_frame(x : Int) -> Int {
  x + 1
}
```

这些属性只是提示，不会改变函数在源码层面的行为。

### `external` 属性

`#external`属性用于标记一个抽象类型为外部类型。

- 对于 Wasm 和 Wasm GC 后端，它将被解释为 `externref`。
- 对于 JavaScript 后端，它将被解释为`any`。
- 对于原生后端，它将被解释为`void*`。

```moonbit
##external
type AttrPtr
```

### `borrow` 和 `owned` 属性

`#borrow` 和 `#owned` 属性用于 FFI 声明，用来描述带引用计数的 MoonBit 参数如何传递给外部代码。这对于 `Bytes`、`String`、`FixedArray[T]` 以及抽象类型等装箱的 MoonBit 值很重要；这些值在 C 和 Wasm 后端上的生命周期由引用计数管理。

当外部函数只在调用期间读取 `param`，且不会存储或返回它时，使用 `#borrow(param)`。借用参数仍由 MoonBit 拥有，因此外部函数不需要为它调用 `moonbit_decref` 或 `$moonbit.decref`。

当外部函数接管 `param` 的所有权时，例如将它存储起来并在之后释放，使用 `#owned(param)`。owned 参数最终必须由外部侧在不再需要时释放。

```moonbit
##borrow(filename)
extern "C" fn open(filename : Bytes, flags : Int) -> Int = "open"
```

完整的调用约定规则见 [FFI 生命周期管理](ffi.md#the-borrow-and-owned-attribute)。

### `as_free_fn` 属性

`#as_free_fn` 属性用于标记一个方法，同时它也被声明为一个自由函数。它还可以改变自由函数的可见性、自由函数的名字，并且提供单独的弃用警告。

```moonbit
##as_free_fn(dec, visibility="pub", deprecated="use `Int::decrement` instead")
##as_free_fn(visibility="pub")
fn Int::decrement(i : Self) -> Self {
  i - 1
}

test {
  let _ = decrement(10)
  let _ = (10).decrement()
}
```

### `callsite` 属性

`#callsite` 属性用于标记发生在调用位置的属性。

它可以是 `autofill`，用于在调用位置自动填充参数 [SourceLoc 和 ArgLoc](fundamentals.md#id1)。

### `skip` 属性

The `#skip` attribute is used to skip a single test block. It can be written as
`#skip` or with a reason, such as `#skip("blocked by external service")`. The
type checking will still be performed.

### Coverage Skip Attribute

`#coverage.skip` 属性会跳过函数内的覆盖率操作。

```moonbit
##coverage.skip
fn platform_specific_helper() -> Unit {
  ()
}
```

它适用于不应影响覆盖率报告的函数，例如特定平台的回退代码，或有意排除在覆盖率统计之外的代码路径。更多细节见[跳过覆盖率](../toolchain/moon/coverage.md#skipping-coverage)。

### `cfg` 属性

`#cfg` 属性用于执行条件编译。例子有：

<!-- MANUAL CHECK -->
```moonbit
##cfg(true)
fn cfg_true() -> Unit {
  ()
}

##cfg(false)
fn cfg_false() -> Unit {
  ()
}

##cfg(target="wasm")
fn cfg_wasm() -> Unit {
  ()
}

##cfg(not(target="wasm"))
fn cfg_not_wasm() -> Unit {
  ()
}

##cfg(all(target="wasm", true))
fn cfg_all() -> Unit {
  ()
}

##cfg(any(target="wasm", target="native"))
fn cfg_any() -> Unit {
  ()
}
```

### 模块属性

`module` 属性用于为 JavaScript 后端声明模块依赖。

在 `cjs` 格式中，它被解释为 `require`，而在 `esm` 格式中，它被解释为 `import`。

<!-- MANUAL CHECK -->
```moonbit
##module("math-utils")
pub extern "js" fn add_from_module(x : Int, y : Int) -> Int = "add"
```

<!-- path: language/ffi.md -->
## 外部函数接口 (FFI)

我们已经介绍的是纯粹的计算。在现实中，需要与真实世界互动。然而，对于每个后端（C、JS、Wasm、WasmGC），“世界”是不同的，并且基于运行时（[Wasmtime](https://wasmtime.dev/)、Deno、浏览器等）。

### 后端

MoonBit 目前有五个后端：

- Wasm
- Wasm GC
- JavaScript
- C
- LLVM（实验性）

#### Wasm

Wasm 是指包含一些 MVP 之后的提案的 WebAssembly，包括：

- 批量内存操作
- 多值
- 引用类型

为了更好的兼容性，`init` 函数会被编译成 [start function](https://webassembly.github.io/spec/core/syntax/modules.html#start-function)，而 `main` 函数会被导出为 `_start`。

##### NOTE
对于 Wasm 后端，所有与外部世界交互的函数都依赖于宿主环境。例如，Wasm 和 Wasm GC 后端的 `println` 函数依赖于导入一个函数 `spectest.print_char`，它在每次调用时打印一个 UTF-16 码元。标准库中的 `env` 包和一些 `moonbitlang/x` 包依赖于 MoonBit 运行时定义的特定宿主函数。如果您想让生成的 Wasm 可移植，请避免使用它们。

#### Wasm GC

Wasm GC 指的是使用垃圾回收提案的 WebAssembly，这意味着数据结构将使用引用类型（例如 `struct` 和 `array`）表示，线性内存不会被默认使用。它还支持其他 MVP 之后的提案，包括：

- 多值
- JS string builtins

为了更好的兼容性，`init` 函数会被编译成 [start function](https://webassembly.github.io/spec/core/syntax/modules.html#start-function)，而 `main` 函数会被导出为 `_start`。

##### NOTE
对于 Wasm 后端，所有与外部世界交互的函数都依赖于宿主环境。例如，Wasm 和 Wasm GC 后端的 `println` 函数依赖于导入一个函数 `spectest.print_char`，它在每次调用时打印一个 UTF-16 码元。标准库中的 `env` 包和一些 `moonbitlang/x` 包依赖于 MoonBit 运行时定义的特定宿主函数。如果您想让生成的 Wasm 可移植，请避免使用它们。

#### JavaScript

JavaScript 后端会生成一个 JavaScript 文件，这个文件可以是一个 CommonJS 模块、ES 模块或 IIFE，具体取决于 [配置](../toolchain/moon/package.md#js-backend-link-options)。

#### C

C 后端会生成一个 C 文件。MoonBit 工具链还会编译项目并根据 [配置](../toolchain/moon/package.md#native-backend-link-options) 生成可执行文件。

#### LLVM

LLVM 后端会生成一个对象文件。该后端是实验性的，不支持 FFI。

### 声明外部类型

您可以利用 `#external` 属性，像这样声明一个外部类型：

```moonbit
##external
type ExternalRef
```

#### Wasm 与 Wasm GC

这将被解释为一个 [`externref`](https://webassembly.github.io/spec/core/syntax/types.html#reference-types)。

#### JavaScript

这将被解释为一个 JavaScript 值。

#### C

这将被解释为 `void*`。

### 声明外部函数

要和外部世界互动，您可以声明外部函数。

##### NOTE
MoonBit 不支持多态外部函数。

##### IMPORTANT
当声明函数时，您需要确保函数签名与实际的外部函数相对应。**当外部函数不返回值时，请使用 `-> Unit`。在 C 中，这对应 `void`；在 Wasm 中，这对应没有返回结果的函数。**

#### Wasm 与 Wasm GC

声明外部函数有两种方式：导入一个函数或编写一个内联函数。

您可以通过模块名和函数名导入一个函数：

```moonbit
fn cos(d : Double) -> Double = "math" "cos"
```

或者，您可以使用 Wasm 语法编写一个内联函数：

```moonbit
extern "wasm" fn identity(d : Double) -> Double =
  #|(func (param f64) (result f64))
```

##### NOTE
编写内联函数时，请不要提供函数名称。

#### JavaScript

声明外部函数有两种方式：导入一个函数或编写一个内联函数。

您可以通过模块名和函数名导入一个函数，它会被解读为 `{模块名}.{函数名}`。例如，

```moonbit
fn cos(d : Double) -> Double = "Math" "cos"
```

会引用函数 `const cos = (d) => Math.cos(d)`

或者，您可以编写一个内联函数，定义一个 JavaScript lambda：

```moonbit
extern "js" fn cos(d : Double) -> Double =
  #|(d) => Math.cos(d)
```

#### C

您可以通过函数名称导入一个外部函数：

```moonbit
extern "C" fn put_char(ch : UInt) -> Unit = "function_name"
```

If a package needs to dynamically link with foreign C library, add `cc-link-flags` to `moon.pkg`. It would be passed to C compiler directly.

```moonbit
options(
  "link": {
    "native": {
      "cc-link-flags": "-l<c library>"
    }
  },
)
```

To define wrapper functions, you can add a C stub file to a package, and add the following to the `moon.pkg` of the package:

```moonbit
options(
  "native-stub": [ 
    // list of stub file names
  ],
)
```

你可能会想要 `#include "moonbit.h"`，这个头文件包含了 MoonBit 的 C FFI 接口中的类型定义和一些实用的辅助函数。这个头文件自身通常位于 `~/.moon/include`，如果想要了解 `moonbit.h` 中有哪些可用的定义，可以直接查看它的内容。

#### 类型

下面的表格展示了一些 MoonBit 类型的底层表示：

#### Wasm

| MoonBit type                       | ABI         |
|------------------------------------|-------------|
| `Bool`                             | `i32`       |
| `Int`                              | `i32`       |
| `UInt`                             | `i32`       |
| `Int64`                            | `i64`       |
| `UInt64`                           | `i64`       |
| `Float`                            | `f32`       |
| `Double`                           | `f64`       |
| constant `enum`                    | `i32`       |
| external type (`#external type T`) | `externref` |
| `FuncRef[T]`                       | `funcref`   |

#### Wasm GC

| MoonBit type                       | ABI                                     |
|------------------------------------|-----------------------------------------|
| `Bool`                             | `i32`                                   |
| `Int`                              | `i32`                                   |
| `UInt`                             | `i32`                                   |
| `Int64`                            | `i64`                                   |
| `UInt64`                           | `i64`                                   |
| `Float`                            | `f32`                                   |
| `Double`                           | `f64`                                   |
| constant `enum`                    | `i32`                                   |
| external type (`#external type T`) | `externref`                             |
| `String`                           | `externref` iff JS string builtin is on |
| `FuncRef[T]`                       | `funcref`                               |

#### JavaScript

| MoonBit type                       | ABI          |
|------------------------------------|--------------|
| `Bool`                             | `boolean`    |
| `Int`                              | `number`     |
| `UInt`                             | `number`     |
| `Float`                            | `number`     |
| `Double`                           | `number`     |
| constant `enum`                    | `number`     |
| external type (`#external type T`) | `any`        |
| `String`                           | `string`     |
| `FixedArray[Byte]`/`Bytes`         | `Uint8Array` |
| `FixedArray[T]` / `Array[T]`       | `T[]`        |
| `FuncRef[T]`                       | `Function`   |

##### NOTE
对于数字类型，`FixedArray[T]` 在将来可能会被迁移到 `TypedArray`。

#### C

| MoonBit type                       | ABI                                    |
|------------------------------------|----------------------------------------|
| `Bool`                             | `int32_t`                              |
| `Int`                              | `int32_t`                              |
| `UInt`                             | `uint32_t`                             |
| `Int64`                            | `int64_t`                              |
| `UInt64`                           | `uint64_t`                             |
| `Float`                            | `float`                                |
| `Double`                           | `double`                               |
| constant `enum`                    | `int32_t`                              |
| abstract type (`type T`)           | pointer (must be valid MoonBit object) |
| external type (`#external type T`) | `void*`                                |
| `FixedArray[Byte]`/`Bytes`         | `uint8_t*`                             |
| `FixedArray[T]`                    | `T*`                                   |
| `FuncRef[T]`                       | Function pointer                       |

##### NOTE
返回 `Unit` 的外部函数对应一个返回 `void` 的 C 函数。如果 `FuncRef[T]` 中 `T` 的返回类型是 `Unit`，那么它指向一个返回 `void` 的函数。

上表中未提及的类型不具有稳定的二进制接口，请尽量避免依赖它们的实际二进制表示。

#### 回调

有时，我们想要将 MoonBit 函数作为回调传递给外部接口。在 MoonBit 中，可以定义闭包。根据 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Closures) 的定义：

> 闭包是由捆绑起来（封闭的）的函数和函数周围状态（词法环境）的引用组合而成。换言之，闭包让函数能访问它的外部作用域。在 JavaScript 中，闭包会随着函数的创建而同时创建。

在一些场合，我们希望将传递一个回调函数，其不捕获任何局部变量。为此，MoonBit 提供了一个特殊的类型 `FuncRef[T]`，它表示类型为 `T` 的无捕获函数。类型为 `FuncRef[T]` 的值必须是类型为 `T` 的无捕获函数，否则会产生 [类型错误](error_codes/E4151.md) 。

在其他情况下，MoonBit 函数参数会被表示为一个函数和一个包含周围状态的对象。

#### Wasm 与 Wasm GC

对于 Wasm 后端，回调函数会被传递为 `externref`，它表示宿主的一个函数。然而，将函数和捕获的数据转换为宿主的函数是非常重要的。

为此，Wasm 模块会在模块 `moonbit:ffi` 下导入一个函数 `make_closure`。这个函数接受一个函数和一个对象，其中函数的第一个参数应该是这个对象；并且应该返回一个宿主的函数。也就是说，宿主负责做部分调用。一个可能的实现是：

```javascript
{ 
  "moonbit:ffi": {
    "make_closure": (funcref, closure) => funcref.bind(null, closure)
  } 
}
```

#### JavaScript

JavaScript 支持闭包，因此这里没有什么特别需要做的。

#### C

有些 C 函数允许在回调函数之外额外提供一些附加数据。例如，假设我们有下面的 C 函数：

```c
void register_callback(void (*callback)(void*), void *data);
```

通过一个小技巧，我们可以向这个 C 函数传递 MoonBit 中的闭包：

```moonbit
extern "C" fn register_callback_ffi(
  call_closure : FuncRef[(() -> Unit) -> Unit],
  closure : () -> Unit
) -> Unit = "register_callback"

fn register_callback(callback : () -> Unit) -> Unit {
  register_callback_ffi(
    fn (f) { f() },
    callback
  )
}
```

类型为 `FuncRef[_]` 的值也可以在 MoonBit 代码中直接调用。这一功能可以用于在原生后端实现动态符号加载或实现 JIT 编译。

#### 自定义常量枚举的整数表示

在所有后端，常量枚举（所有构造器都没有参数的枚举）都会被编译成整数。在此基础上，MoonBit 允许用户自定义常量枚举的构造器的整数表达式。只需在构造器的声明后加上 `= <整数字面量>` 即可：

```moonbit
enum SpecialNumbers {
  Zero = 0
  One
  Two
  Three
  Ten = 10
  FourtyTwo = 42
}
```

如果一个构造器没有用户指定的整数值，默认的值是上一个构造器的值加一。（第一个构造器的默认值是 `0`）。自定义整数表示的功能在绑定一些 C 库里的 flag 是很有用。

### 导出函数

对于既不是方法也不是多态的公开函数，可以通过配置 [链接选项](../toolchain/moon/package.md#link-options) 中的 `exports` 字段来导出它们。

```moonbit
options(
  "link": {
    "<backend>": {
      "exports": [ "add", "fib:test" ]
    }
  }
)
```

上述例子中导出函数 `add` 和 `fib`，其中 `fib` 会被导出为 `test`。

#### Wasm 与 Wasm GC

##### NOTE
这仅对配置它的包有效，即它不会影响下游包。

#### JavaScript

##### NOTE
这仅对配置它的包有效，即它不会影响下游包。

还有另一个 `format` 选项可以导出为 CommonJS 模块（`cjs`）、ES 模块（`esm`）或立即调用的函数表达式（`iife`）。

#### C

##### NOTE
这仅对配置它的包有效，即它不会影响下游包。

目前不支持重命名导出的函数。

### 生命周期管理

MoonBit 是一门具有垃圾回收的编程语言。因此在处理外部对象或将 MoonBit 对象传递给宿主时，必须牢记生命周期管理。目前，MoonBit 对 Wasm 后端和 C 后端使用引用计数。对于 Wasm GC 后端和 JavaScript 后端，复用运行时的垃圾回收机制。

#### 外部对象的生命周期管理

在 MoonBit 中处理来自外部的对象和资源时，需要及时释放这些外部对象占用的内存和资源以避免泄漏。

##### NOTE
仅限 C 后端

`moonbit.h` 中提供了一个实用的函数 `moonbit_make_external_object`，它可以借助 MoonBit 的自动内存管理系统来管理外部对象的生命周期：

```c
void *moonbit_make_external_object(
  void (*finalize)(void *self),
  uint32_t payload_size
);
```

`moonbit_make_external_object` 会创建一个大小为 `payload_size + sizeof(finalize)` 的新的 MoonBit 对象，这个对象的内存布局如下：

```default
| MoonBit 对象头 | ... 外部数据 | 释放资源的回调 |
                 ^
                 |
                 |_
                    `moonbit_make_external_object` 返回的指针

```

因此，`moonbit_make_external_object` 返回的指针可以直接当作指向外部数据的指针使用。当 MoonBit 的自动内存管理系统发现 `moonbit_make_external_object` 返回的对象生命周期已经结束时，它会以对象自身为参数，调用创建对象时提供的 `finalize` 函数来释放这个对象占有的外部资源。

##### NOTE
`finalize` **绝对不能** 释放对象自身，因为这部分工作由 MoonBit 运行时负责。

在 MoonBit 侧，`moonbit_make_external_object` 返回的对象应当被绑定到  *抽象* 类型，即用 `type T` 语法声明的类型。这样一来，MoonBit 的内存管理系统就不会无视这个对象。

#### MoonBit 对象的生命周期管理

当通过函数将 MoonBit 对象传递给宿主时，必须注意 MoonBit 对象本身的生命周期管理。如前所述，MoonBit 的 Wasm 后端和 C 后端使用编译器优化的引用计数来管理对象的生命周期。为了避免内存错误或泄漏，FFI 函数必须正确维护 MoonBit 对象的引用计数。

##### NOTE
仅限 C 后端和 Wasm 后端。

##### 引用计数的调用约定

MoonBit 的引用计数默认使用被调用者持有所有权的调用约定。也就是说，被调用的函数需要调用 `moonbit_decref` 函数来释放它的参数。如果参数被多次使用，被调用的函数需要调用 `moonbit_incref` 函数来增加引用计数。下面是不同场合下维护正确引用计数需要做的操作：

| 场合                 | 操作          |
|--------------------|-------------|
| 读取字段/元素            | 什么都不做       |
| 存储进数据结构            | 调用 `incref` |
| 作为参数传递给 MoonBit 函数 | 调用 `incref` |
| 作为参数传递给其他外部函数      | 什么都不做       |
| 作为返回值被返回           | 什么都不做       |
| 作用域结束（且没有返回）       | 调用 `decref` |

下面的例子是一个正确维护引用计数的、标准的打开文件的 `open` 函数的绑定：

```moonbit
extern "C" fn open(filename : Bytes, flags : Int) -> Int = "open_ffi"
```

```c
int open_ffi(moonbit_bytes_t filename, int flags) {
  int fd = open(filename, flags);
  moonbit_decref(filename);
  return fd;
}
```

##### 被管理的类型

下面的类型不是分配在堆上的，不需要管理生命周期：

- 内置数字类型，例如 `Int` 和 `Double`
- 常量枚举（所有构造器都不带参数的枚举）

下面的类型总是分配在堆上的，并且需要引用计数：

- `FixedArray[T]`, `Bytes` and `String`
- 抽象类型（`type T`）

外部类型（`#external type T`）也被分配在堆上，但它们表示外部指针，因此 MoonBit 不会对它们执行任何引用计数操作。

`struct`/有参数的 `enum` 的内存表示是不稳定的。

##### borrow 和 owned 标记

通过 FFI 传递参数时，参数的所有权可能会被保留，也可能不会。`#borrow` 和 `#owned` 标记可以用来指定这两种情况。

##### WARNING
我们正在将默认语义从 `#owned` 迁移到 `#borrow`。

`#borrow` 和 `#owned` 的语法如下：

```moonbit
##borrow(params..)
extern "C" fn c_ffi(..) -> .. = ..
```

其中，`params` 是 `c_ffi` 的参数列表的一个子集。

被 `#borrow` 标记的参数会使用基于借用的调用约定，也就是说，被调用的函数不需要对这些参数调用 `decref`。如果 FFI 函数只会在运行期间读取它的参数（不会返回它的参数或把它们存进数据结构），那么使用 `#borrow` 标记可以直接绑定 FFI 函数。上面的 `open` 的例子可以使用 `#borrow` 标记来简化：

```moonbit
##borrow(filename)
extern "C" fn open(filename : Bytes, flags : Int) -> Int = "open"
```

这里不再需要 C 胶水函数：我们直接绑定到了原版 `open`。`#borrow` 标记保证了这个简化的版本依然能正确维护引用计数。

即使由于某些其他原因依然需要写 C 胶水函数，`#borrow` 标记也可以用于简化 C 胶水内部的生命周期管理。下面是不同场合下，正确维护 **借用参数** 的引用计数需要做的操作：

| 场合                                | 操作          |
|-----------------------------------|-------------|
| 读取字段/元素                           | 什么都不做       |
| 存储进数据结构                           | 调用 `incref` |
| 作为参数传递给 MoonBit 函数                | 调用 `incref` |
| 传递给其他 C 函数 / `#borrow` MoonBit 函数 | 什么都不做       |
| 作为返回值被返回                          | 调用 `incref` |
| 作用域结束（且没有返回）                      | 什么都不做       |

`#owned` 语义与之相反，表示参数会被 FFI 函数存储，并且需要在稍后手动调用 `decref`。一个使用场景是注册回调函数，其中闭包会被 **持有所有权**。

<!-- path: language/async-experimental.md -->
## 异步编程支持

MoonBit 采用了一种类似 [Kotlin](https://kotlinlang.org/docs/coroutines-overview.html) 的、基于协程的方式来支持异步编程。MoonBit 的异步编程支持由两部分组成：MoonBit 编译器中对 `async` 函数的支持，以及 MoonBit 的官方异步运行时 `moonbitlang/async`。目前，`moonbitlang/async` 对 native 后端有最好的支持，对 JavaScript 有有限的支持，暂时不支持 WebAssembly 后端。`moonbitlang/async` 的 API 仍在开发中，可能在未来有变动。

<!-- We highly appreciate any feedback or experiment with current design. -->

### 在 MoonBit 中使用异步编程

要在 MoonBit 中使用 `moonbitlang/async` 异步编程，首先需要运行 `moon add moonbitlang/async@0.19.2` 来把 `moonbitlang/async`添加到当前项目的依赖中。可以在 `moon.mod.json` 中添加 `"preferred-target": "native"`来默认使用 `moonbitlang/async` 支持最好的 native 后端。这之后，只需要在 `moon.pkg` 的 `import` 中添加 `moonbitlang/async` 的各个包，就能使用 `moonbitlang/async` 中的各种异步编程 API 了。如果你想看一个以工作流为主线的示例，可以参考[原生 CLI 快速开始](../tutorial/cli-quickstart.md)。

可以在 [mooncakes.io](https://mooncakes.io/docs/moonbitlang/async) 查询`moonbitlang/async` 中的包列表以及它们各自的详细文档，还可以在 `moonbitlang/async` 的 GitHub 仓库中找到一些简单的 [例子](https://github.com/moonbitlang/async/tree/main/examples)。这篇文章会介绍 `moonbitlang/async` 的基本概念和其中最重要的一些 API。

### 异步函数

异步函数用 `async` 关键字定义。他们隐式地 [抛出错误](error-handling.md#throwing-errors) ，而如果不抛出错误则需要显式地声明 `noraise`。

```moonbit
async fn my_async_function() -> String {
  let (response, body) = @http.get("https://www.moonbitlang.cn")
  guard response.code is (200..<300) else {
    fail("server responded with \{response.code} \{response.reason}")
  }
  body.text()
}

```

由于 MoonBit 是一门静态类型语言，编译器会跟踪异步函数的调用，因此你可以像调用普通函数一样调用异步函数。MoonBit IDE 会用不同的样式来高亮显示异步函数的调用。例如，如果在 MoonBit IDE 中打开上面的代码片段，就会看到 `@http.get` 函数被渲染成了斜体 + 下划线的效果。

异步函数只能在异步函数中被调用。调用一个 `async` 函数会使调用者阻塞并等待被调用的函数返回，类似其他语言中的 `await`。

MoonBit 对异步编程有原生的支持。可以用 `async fn main` 来声明异步的程序入口，或者用 `async test` 来编写异步测试。默认情况下，多个异步测试可以并行地同时运行。注意使用 `async fn main` 或 `async test` 需要在 `moon.pkg` 中添加 `moonbitlang/async` 作为依赖。

### 结构化并发与任务组

如果一段异步程序除了直接调用其他异步函数没有做别的事情，那么它的控制流会是线性的，和普通的同步程序没有区别。异步程序和同步程序最本质的不同，在于异步程序能同时创建多个任务，并让它们同时运行。由于多个异步任务可以同时运行，异步程序的控制流会复杂许多因此，创建多个任务的能力也带来了新的挑战：如何健壮地管理任务。

`moonbitlang/async` 采用了  *结构化并发* 这一编程范式来解决任务管理的问题，并提升异步程序的健壮性。在 `moonbitlang/async` 中，新任务只能在一个  *任务组* 中被创建。而任务组必须通过 `@async.with_task_group` 函数创建：

```moonbit
async fn[Result] with_task_group(
  f : async (@async.TaskGroup[Result]) -> Result,
) -> Result
```

`with_task_group` 函数会创建一个新的任务组，在其中创建一个新的任务，并在这个任务中以任务组自己为参数运行 `f`。`f` 可以通过 `spawn_bg` 等方法来使用任务组创建更多任务：

```moonbit
/// 在任务组中创建一个新的任务并在后台运行它
fn[Result] @async.TaskGroup::spawn_bg(
  group : TaskGroup[Result],
  f : async () -> Unit,
  ...
) -> Unit

```

结构化并发范式的“魔法”就在 `with_task_group` 需要遵守的一条规则之中：

> 只有当任务组中的所有任务都结束了，`with_task_group` 才会返回

`with_task_group` 会保证上述规则在所有情况下都成立。正常情况下，`with_task_group` 会等待所有任务正常结束。如果 `with_task_group` 因为某些原因需要提前结束，例如某个任务失败了（默认情况下，如果任务组中的某个任务失败了，`with_task_group` 也会立刻失败，从而确保错误不会被不小心忽略），它会取消所有还在运行的任务，并等待它们的清理工作完成。结果上看，`with_task_group` 的规则保证了  *孤儿任务*（由于忘记被取消，明明已经没用了但仍在运行的任务）在 `moonbitlang/async` 中不可能出现。

下面是一个使用 `with_task_group` 创建多个任务，并让它们同时运行的例子：

```moonbit
async test "with_task_group" {
  let log = []
  @async.with_task_group(group => {
    group.spawn_bg(() => {
      for _ in 0..<3 {
        log.push("task #1 tick")
        @async.sleep(200) // sleep for 200ms
      }
    })
    group.spawn_bg(() => {
      @async.sleep(100)
      for _ in 0..<3 {
        log.push("task #2 tick")
        @async.sleep(200)
      }
    })
  })
  json_inspect(log, content=[
    "task #1 tick", "task #2 tick", "task #1 tick", "task #2 tick", "task #1 tick",
    "task #2 tick",
  ])
}
```

`with_task_group` 是一个非常强大的构造。它能模拟许多其他异步控制流原语。例如，下面是一个使用 `with_task_group` 实现的、给一个异步函数添加超时限制的辅助函数：

```moonbit
async fn with_timeout(timeout : Int, f : async () -> Unit) -> Unit {
  @async.with_task_group(group => {
    group.spawn_bg(no_wait=true, () => {
      @async.sleep(timeout)
      raise Failure::Failure("timeout!")
    })
    f()
  })
}
```

这段代码非常简短。但是 `with_task_group` 的语义保证了这个简单的小函数在所有边界情况上都能有正确的行为：

- 如果 `f` 在超时之前顺利返回，由于创建负责计时的任务时设置了 `no_wait=true`，`with_task_group` 不会等待计时任务完成，会立即准备结束。为了维护 `with_task_group` 的性质，计时任务会立即被取消并结束。因此，`with_timeout(.., f)` 会在 `f` 返回时立刻返回，不会有不必要的延迟。
- 如果 `f` 失败了，它抛出的错误会传播到整个 `with_task_group` 上。此时，计时任务同样会被自动取消。
- 如果 `f` 超时了，计时任务会抛出一个错误，终止整个任务组。此时，`f` 会被自动取消，因此不会有资源泄漏。

### 通过取消机制实现模块化的异步程序

在前面的章节中，我们多次提到了“取消”这一概念。在异步编程中，能够取消一个正在运行的任务是非常重要的。在 `moonbitlang/async` 中，包括 `with_task_group` 在内的所有异步操作默认都是可取消的。所以，当用户使用这些基础操作组合出大规模的异步程序时，无论最终得到的程序有多复杂，它依然自动是可取消的。

当一段异步代码被取消时，取消信号会变成从这段代码中断的地方抛出的错误。所以，用户的程序无需对取消进行特殊处理：取消信号会通过错误处理机制自动传播，并触发 `defer` 表达式和 `catch` 中的各种清理代码。

取消任意异步代码的能力使 MoonBit 中的异步程序变得高度模块化。`moonbitlang/async` 包提供了许多使用的异步辅助函数，例如给一段异步代码加上超时、自动重试等。这些辅助函数都依赖于取消机制。例如，下面是一段尝试在时限内进行一次 HTTP 请求，并允许至多三次重试的程序：

```moonbit
async fn make_request() -> String {
  @async.retry(Immediate, max_retry=3, () => {
    @async.with_timeout(1000, () => {
      let (response, body) = @http.get("https://www.moonbitlang.com")
      guard response.code is (200..<300) else {
        fail("the HTTP request is not successful")
      }
      body.text()
    })
  })
}
```

### 和外部世界交互

除了各种异步编程原语，`moonbitlang/async` 还提供了一个 [性能优秀](https://www.moonbitlang.com/blog/moonbit-async#performance-comparison) 的 IO 事件循环，以及一系列丰富的异步 IO 操作，包括 `http`/`https`、文件 IO、网络 IO 和创建外部进程。可以在 [mooncakes.io](https://mooncakes.io/docs/moonbitlang/async) 找到完整的 API 列表和它们的详细文档，也可以在 `moonbitlang/async` 的 [GitHub 仓库](https://github.com/moonbitlang/async/tree/main/examples) 找到一些简单示例。下面是一个小例子：

```moonbit
async fn download_file(url : String, file_name : String) -> Unit {
  // 流式地处理内容以节省内存
  let (_response, body) = @http.get_stream(url)
  defer body.close()
  let out_file = @fs.create(file_name, permission=0o644)
  defer out_file.close()
  out_file.write_reader(body)
}

```

### JavaScript 支持

虽然 `moonbitlang/async` 对 native 后端支持最好，它也对 JavaScript 后端有基本的支持：

- 所有 IO 无关的 API，例如任务组、超时等，都可以在 JavaScript 后端使用
- JavaScript 后端无法使用 IO 相关的 API，因为不是所有 JavaScript 环境（例如浏览器）都支持这些 IO 操作
- `moonbitlang/async/js_async` 包可以用来和 JavaScript promise 互动。它提供了等待一个 JavaScript promise 和把一个 MoonBit `async` 函数包装成一个 JavaScript promise 的功能。利用这个功能，用户可以自行绑定目标 JavaScript 环境中支持的异步操作。

关于和 JavaScript 互动的更多信息，可以参考 `moonbitlang/async/js` 的 [mooncakes.io 页面](https://mooncakes.io/docs/moonbitlang/async/js_async)。

<!-- path: language/verification.md -->
## 形式化验证

MoonBit 通过 `moon prove` 提供实验性的形式化验证支持。你可以编写可执行的 MoonBit 代码，为这些代码陈述逻辑性质，并借助 SMT 求解器证明生成的证明任务。

从整体上看，工作流程如下：

1. 在 `.mbt` 文件中编写可执行代码。
2. 在 `.mbtp` 文件中编写谓词、逻辑辅助函数和引理。
3. 在包中启用证明模式。
4. 运行 `moon prove`。

### 概览示例：二分查找

下面这个包简要展示了 MoonBit 中各项验证机制如何配合工作。它展示了：

- 在包级别启用证明
- `.mbtp` 中的逻辑侧谓词
- `.mbt` 中的程序侧函数
- 前置条件与后置条件
- 循环不变量与 `proof_yield`
- 局部的 `proof_assert` 步骤
- 用 `proof_reasoning` 对循环进行面向证明的说明

首先，为该包启用证明：

```moonbit
options(
  "proof-enabled": true,
)
```

然后在 `.mbtp` 中定义逻辑侧规范：

```moonbit
predicate in_bounds(xs : FixedArray[Int], i : Int) {
  (0 <= i) && (i < xs.length())
}

predicate sorted(xs : FixedArray[Int]) {
  ∀ i : Int, ∀ j : Int,
    in_bounds(xs, i) && in_bounds(xs, j) && (i <= j) →
      xs[i] <= xs[j]
}

predicate binary_search_ok(xs : FixedArray[Int], key : Int, result : Option[Int]) {
  match result {
    None => ∀ i : Int, in_bounds(xs, i) → xs[i] != key
    Some(result) => in_bounds(xs, result) && xs[result] == key
  }
}
```

最后，在 `.mbt` 中实现可执行函数：

```moonbit
pub fn binary_search(
  xs : FixedArray[Int],
  key : Int,
) -> Int? where {
  proof_require: sorted(xs),
  proof_ensure: result => binary_search_ok(xs, key, result),
} {
  for i = 0, j = xs.length(); i < j; {
    let h = i + (j - i) / 2
    if xs[h] < key {
      proof_assert ∀ idx : Int,
        (0 <= idx) && (idx < h + 1) → xs[idx] < key
      continue h + 1, j
    } else if key < xs[h] {
      proof_assert ∀ idx : Int,
        (h <= idx) && (idx < xs.length()) → key < xs[idx]
      continue i, h
    } else {
      proof_assert xs[h] == key
      break Some(h)
    }
  } nobreak {
    None
  } where {
    proof_invariant: 0 <= i,
    proof_invariant: i <= j,
    proof_invariant: j <= xs.length(),
    proof_invariant: ∀ idx : Int, (0 <= idx) && (idx < i) → xs[idx] < key,
    proof_invariant: ∀ idx : Int, (j <= idx) && (idx < xs.length()) → key < xs[idx],
    proof_yield: res => binary_search_ok(xs, key, res),
    proof_reasoning: (
      #| The loop maintains a candidate window `[i, j)`.
      #| Every index before `i` is known to hold a value `< key`, and every
      #| index from `j` onward is known to hold a value `> key`.
      #|
      #| At midpoint `h` there are three cases:
      #|   - `xs[h] < key`: move the left boundary to `h + 1`
      #|   - `key < xs[h]`: move the right boundary to `h`
      #|   - otherwise, the element at `h` is equal to `key`
      #|
      #| If the loop exits normally, the exclusion invariants cover the whole
      #| array, so no index can contain `key`.
    ),
  }
}
```

这个示例已经展示了经过验证的 MoonBit 代码的主要结构：

- `sorted` 与 `binary_search_ok` 是逻辑侧谓词
- `binary_search` 是可执行的程序侧代码
- `where { ... }` 块将逻辑侧契约附加到函数上
- `proof_assert` 记录有助于证明器求解的局部条件
- 循环不变量描述逐步收缩的搜索窗口
- `proof_reasoning` 用结构化文字记录证明思路

本页后续部分会更详细地解释这些组成部分，并介绍 `#proof_pure`、`proof_decrease`、`proof_axiomatized`、基于模型的验证，以及可信边界等附加特性。

### 环境准备

#### 环境配置

`moon prove` 依赖外部的 Why3 验证工具链。MoonBit 会将启用了证明的包转换为 Why3，随后由 Why3 将证明任务分派给一个或多个外部求解器。

#### Why3

在 MoonBit 中运行形式化验证必须安装 Why3。

- 推荐通过 `opam` 安装 Why3。
- 推荐固定使用的版本为 `1.7.2`。

使用 `opam` 更容易让 Why3 与当前 MoonBit 证明流水线所期望的版本保持一致。

#### 外部求解器

还必须至少安装一个外部求解器。

MoonBit 当前支持：

- `z3`
- `cvc5`
- `alt-ergo`

安装多个求解器有助于提高证明覆盖率，但一个可用的环境只需要其中任意一个即可。

#### 在包中启用证明

证明支持是按包在 `moon.pkg` 中启用的，如上面的概览示例所示。

启用后，该包既可以包含普通的 MoonBit 源文件，也可以包含面向证明的 `.mbtp` 文件。如果一个模块中的多个包都包含证明，那么每个这样的包都必须分别启用它。

### 包含验证的代码的结构

#### 源码布局

面向验证的包通常会分成两层：

- `.mbt`：可执行代码、契约、循环不变量和局部证明步骤
- `.mbtp`：谓词、抽象模型、不变量和引理

这种拆分方式在保持运行时代码可读性的同时，也让证明结构更加明确。在 [二分查找概览示例]() 中，谓词位于 `.mbtp` 中，而可执行的搜索函数位于 `.mbt` 中。

#### 程序侧与逻辑侧

MoonBit 的验证系统明确区分程序侧与逻辑侧。

- 程序侧是可执行的 MoonBit 代码。
- 逻辑侧用于编写规范与证明，它可能根本不对应任何运行时代码。

`.mbt` 文件中的定义属于程序侧，`.mbtp` 文件中的定义属于逻辑侧。

这种区分很重要，因为程序侧定义与逻辑侧定义是分离的：

- 程序侧定义是可执行的，可以被普通 MoonBit 代码调用
- 逻辑侧定义用于规范与证明
- `.mbtp` 中的逻辑侧定义不是普通的运行时函数
- 程序侧代码在执行过程中不会调用逻辑侧定义
- 逻辑侧规范通常也不会任意调用程序侧定义

正因为有这种分离，带证明的包通常会同时包含：

- 供执行时使用的程序侧辅助函数
- 供契约使用的逻辑侧谓词或模型函数

当某个定义需要同时在两侧可见时，就可以使用 `#proof_pure`。

在 `.mbt` 文件中，契约和证明注解是逻辑侧推理嵌入程序代码的位置。具体来说：

- `proof_require` 和 `proof_ensure` 是附加在程序函数上的逻辑侧规范
- `proof_assert` 声明一个必须在该位置被证明的逻辑事实
- 诸如 `proof_invariant`、`proof_yield` 和 `proof_reasoning` 之类的循环注解，都是关于可执行循环的逻辑侧陈述

### 编写规范与证明

#### `.mbt` 中的契约

MoonBit 使用 `where { ... }` 子句来编写函数契约。在二分查找概览示例中，函数契约直接写在程序侧定义上：

- `proof_require` 声明前置条件。
- `proof_ensure` 声明后置条件。
- `result` 指代函数结果。

在可执行代码内部，可以用 `proof_assert` 记录中间事实，帮助证明器把实现与规范关联起来。

这通常就是把程序侧实现事实连接到逻辑侧谓词和后置条件的地方。

#### 证明专用注解

除了 `proof_require`、`proof_ensure` 和 `proof_assert` 之外，MoonBit 还提供了一些证明专用注解，用于控制证明流水线如何处理这些定义。

#### `#proof_pure`

`#proof_pure` 从验证器的角度将一个函数标记为纯函数。这适用于那些计算逻辑量的辅助函数：它们会出现在谓词和后置条件中，同时又需要在程序侧可见。

```moonbit
##proof_pure
fn height(t : Tree) -> Int {
  match t {
    Empty => 0
    Node(_, _, _, h) => h
  }
}
```

同一个辅助函数随后就可以在规范中使用：

```moonbit
predicate cached_height_ok(t : Tree, result : Int) {
  result == height(t)
}
```

`#proof_pure` 的动机在于，有些计算天然同时属于两侧。例如树的 `height` 这样的辅助函数，在可执行代码中很有用，而规范也可能需要引用同一个量。

如果没有 `#proof_pure`，通常就意味着：

- 写一个供执行使用的程序侧定义
- 再写一个供规范使用的逻辑侧定义
- 在使用之前还要证明这两个定义是等价的

`#proof_pure` 允许一个无副作用的 MoonBit 定义被程序代码和面向证明的逻辑共同使用，从而避免这种重复。

在更大的验证包中，`#proof_pure` 常用于诸如高度这类结构度量、排序函数、汇总函数，以及其他应当像数学函数一样工作的面向证明的计算。

目前，`#proof_pure` 辅助函数更适合视为纯规范辅助函数，而不是带完整契约的已验证函数。尤其是，直接给 `#proof_pure` 定义附加普通验证契约的支持仍然有限。

#### `proof_decrease`

`proof_decrease` 为递归定义提供终止度量。

```moonbit
pub fn countdown(n : Int) -> Int where {
  proof_decrease: n,
  proof_require: 0 <= n,
  proof_ensure: result => 0 <= result,
} {
  if n <= 0 {
    0
  } else {
    countdown(n - 1)
  }
}
```

当证明器无法仅从函数体中直接看出结构递归或数值度量时，这个注解尤其重要。

#### `proof_axiomatized`

`proof_axiomatized` 将一个带契约的函数或引理标记为“被假定成立”，而不是“被证明成立”。

当某个定义需要供后续证明使用，但其实现或证明被有意留在当前验证边界之外时，这会很有用。

在实践中，应当谨慎使用 `proof_axiomatized`：

- 用于函数时，表示验证器会直接假定所声明的契约成立
- 用于引理时，表示验证器会直接假定所声明的结论成立

例如：

```moonbit
pub fn assumed_nonnegative(x : Int) -> Int where {
  proof_axiomatized: true,
  proof_require: 0 <= x,
  proof_ensure: result => 0 <= result,
} {
  x
}
```

当证明仍在开发过程中时，这类声明可以作为临时桥梁；或者当可信边界是有意且明确设定时，也可以使用它。关键在于，`moon prove` 会把该契约当作假设来使用，而不是从函数体出发对其进行证明。

由于这些假设不会被 `moon prove` 消解，它们就会成为已验证包可信边界的一部分。

#### `.mbtp` 中的谓词与引理

逻辑性质定义在 `.mbtp` 文件中。在 [二分查找概览示例]() 中，逻辑侧定义了：

- 把 `in_bounds` 作为基本索引谓词
- 把 `sorted` 作为输入数组的前置条件
- 把 `binary_search_ok` 作为把结果与数组及键值关联起来的后置条件

例如，`.mbtp` 中的谓词可以定义契约所使用的逻辑词汇：

```moonbit
predicate in_bounds(xs : FixedArray[Int], i : Int) {
  (0 <= i) && (i < xs.length())
}

predicate sorted(xs : FixedArray[Int]) {
  ∀ i : Int, ∀ j : Int,
    in_bounds(xs, i) && in_bounds(xs, j) && (i <= j) →
      xs[i] <= xs[j]
}

predicate binary_search_ok(xs : FixedArray[Int], key : Int, result : Option[Int]) {
  match result {
    None => ∀ i : Int, in_bounds(xs, i) → xs[i] != key
    Some(result) => in_bounds(xs, result) && xs[result] == key
  }
}
```

对于更大的验证包，`.mbtp` 还会包含：

- 如 `model(x)` 这样的抽象模型函数
- 如 `bridge_inv(x)` 或 `sparse_array_inv(x)` 这样的表示不变量
- 如 `deposit_post(...)` 这样的具名后置条件
- 可复用的引理

引理是仅用于证明的声明，用来表达可复用的事实。当证明器可以直接消解时，较小的引理可以拥有空函数体；而更大的引理则可以使用递归的证明结构：

```moonbit
lemma height_nonneg_lemma(t : Tree) where {
  proof_decrease: t,
  proof_require: avl_inv(t),
  proof_ensure: 0 <= height(t),
} {
  match t {
    Empty => ()
    Node(l, _x, r, _h) => {
      height_nonneg_lemma(l)
      height_nonneg_lemma(r)
    }
  }
}
```

在这个例子中，`avl_inv` 是 AVL 树的表示不变量，而这个引理通过对 `t` 进行结构递归来证明 `height(t)` 非负。

引理体会随着求解器所需的证明引导程度而有所不同：

- 有些引理的函数体为空，因为验证器可以直接完成证明
- 有些引理会使用一连串 `proof_assert` 步骤来显式给出中间事实
- 递归引理还可以调用其他引理，包括在更小输入上递归调用自身

#### 循环不变量

循环通过循环的 `where { ... }` 块中的证明专用子句来验证。在 [二分查找概览示例]() 中，循环不变量说明了：

- 当前搜索窗口始终是一个合法的区间 `[i, j)`
- `i` 之前的每个索引都包含 `< key` 的值
- 从 `j` 开始的每个索引都包含 `> key` 的值

最常见的循环证明注解有：

- `proof_invariant`：在每次迭代边界都必须成立的事实
- `proof_yield`：对由 `break` 产出的值必须成立的事实
- `proof_reasoning`：解释循环为何正确的面向证明的说明

在实践中，好的循环不变量通常描述当前搜索窗口、前缀或已处理区域，而不是一次性描述整个算法。

#### 基于模型的验证

对于数据结构和有状态系统，最有用的模式通常是：

1. 定义一个抽象模型，
2. 定义一个表示不变量，
3. 针对该抽象模型来为每个操作编写规范。

AVL 树就是这种风格的一个代表性例子：

```moonbit
fn Tree::model(self : Tree) -> Fset[Int] {
  match self {
    Empty => Fset::empty()
    Node(l, x, r, _) => l.model().union(r.model()).add(x)
  }
}

predicate avl_inv(t : Tree) {
  match t {
    Empty => true
    Node(l, x, r, h) =>
      avl_inv(l) &&
      avl_inv(r) &&
      (∀ y : Int, l.model().mem(y) → y < x) &&
      (∀ y : Int, r.model().mem(y) → x < y) &&
      h == 1 + max2(height(l), height(r)) &&
      -1 <= height(l) - height(r) &&
      height(l) - height(r) <= 1
  }
}
```

这里：

- `model()` 把具体的树映射为它的抽象有限集合视图
- 内联的量词条件描述了该抽象模型上的顺序约束
- `avl_inv` 将递归的良构性、搜索树顺序以及缓存高度的正确性绑定在一起

与在每个契约中直接写庞大的内联布尔公式相比，这种风格的可扩展性要好得多。每个操作都可以基于抽象模型和不变量来编写规范，而实现部分则通过证明局部事实，最终推出相应的具名后置条件。

#### 推荐风格

- 把可执行逻辑放在 `.mbt` 中，把证明逻辑放在 `.mbtp` 中。
- 将程序侧和逻辑侧视为两个不同的层次，它们之间只通过一个狭窄的桥梁连接。
- 优先使用具名谓词，而不是大型内联公式。
- 使用小而稳定的不变量和后置条件，例如 `*_inv` 与 `*_post`。
- 当证明器需要帮助时，在重要的构造步骤、分支和循环更新之后加入 `proof_assert`。
- 从简单的算法证明开始，然后再过渡到针对数据结构和协议的基于模型的验证。
- 谨慎设计递归辅助函数：`#proof_pure` 和 `proof_decrease` 往往决定了一个证明是否仍然易于维护。

### 运行验证

#### `moon prove` 会检查什么

运行 `moon prove` 会要求验证器证明如下几类义务：

- 函数前置条件足以保证函数体可以安全执行
- 后置条件在每一条返回路径上都成立
- `proof_assert` 语句是有效的
- 循环不变量初始成立，并且在循环过程中得以保持
- 当循环形式支持时，循环的终止度量会递减
- 证明所需的边界检查和类似的安全性质

#### 运行验证器

在模块根目录下运行：

```bash
moon prove
```

这会尝试证明当前模块中所有启用了证明的包。

如果只想证明一个包，可以传入它的路径：

```bash
moon prove path/to/package
```

在这种定向模式下，MoonBit 只会尝试证明被选中的包；它的依赖会被当作假设，而不会作为同一条命令的一部分重新证明。

MoonBit 会把启用了证明的包转换为 Why3，并调用已配置的证明器处理生成的验证条件。

`moon check` 和 `moon prove` 在这里承担不同的职责。`moon check` 负责把包作为 MoonBit 代码进行校验，而 `moon prove` 则尝试消解它的证明义务。

### 可信模型与限制

#### 受信假设

和任何验证系统一样，MoonBit 的证明体系也有一个可信边界：某些事实由前端和后端直接假定，而不是在用户代码内部加以证明。

任何用户编写且标记为 `proof_axiomatized` 的项目，也属于这个可信边界。

当前主要的可信假设包括：

- 验证所推理的是数学整数，而不是机器整数
- 任何标记为 `proof_axiomatized` 的项目都会被当作假设，而不是被证明

对整数来说，这意味着证明义务是在一个无界整数模型下进行检查的。因此：

- 算术证明目前不会建模运行时溢出
- 一个程序在证明模型中可能是正确的，但在实际执行时仍然需要显式的范围约束
- 机器整数验证已经在计划中，但目前还不是默认模型

#### 当前状态

MoonBit 中的形式化验证仍处于实验阶段。表面语法、证明器集成和证明易用性都还在快速演进，因此最好把它视为一种高级特性，主要面向那些确实能从强而可机检的保证中受益的包。

#### 推荐的验证风格

MoonBit 当前的验证器最适合函数式的证明风格，以及基于模型的规范。

诸如局部可变状态和原地 `FixedArray` 更新这样的命令式特性虽然受支持，但它们在验证中的使用限制更多：

- 可变状态应当保持为局部使用
- `FixedArray` 的逃逸式使用目前还不受支持
- 当两种写法都可行时，优先选择函数式风格的程序

### 进一步阅读

如果想查看更多已验证程序和可复用的面向证明的库，请参考 [`moonbit-community/verified`](https://github.com/moonbit-community/verified) 仓库。

<!-- path: language/error_codes/index.md -->
## 错误代码索引

##### WARNING
该错误代码索引目前正在编写中。

许多条目目前只有非常简短的错误代码描述。我们非常欢迎您通过向 [moonbitlang/moonbit-docs](https://github.com/moonbitlang/moonbit-docs) 提交 Pull Request 的方式来扩充条目内容。

此页面列出了所有 MoonBit 编译器会报告的错误。

* [E0001](E0001.md)
* [E0002](E0002.md)
* [E0003](E0003.md)
* [E0004](E0004.md)
* [E0005](E0005.md)
* [E0006](E0006.md)
* [E0007](E0007.md)
* [E0008](E0008.md)
* [E0009](E0009.md)
* [E0010](E0010.md)
* [E0011](E0011.md)
* [E0012](E0012.md)
* [E0013](E0013.md)
* [E0014](E0014.md)
* [E0015](E0015.md)
* [E0016](E0016.md)
* [E0017](E0017.md)
* [E0018](E0018.md)
* [E0020](E0020.md)
* [E0021](E0021.md)
* [E0022](E0022.md)
* [E0023](E0023.md)
* [E0024](E0024.md)
* [E0025](E0025.md)
* [E0026](E0026.md)
* [E0027](E0027.md)
* [E0028](E0028.md)
* [E0029](E0029.md)
* [E0030](E0030.md)
* [E0031](E0031.md)
* [E0032](E0032.md)
* [E0033](E0033.md)
* [E0034](E0034.md)
* [E0035](E0035.md)
* [E0036](E0036.md)
* [E0037](E0037.md)
* [E0038](E0038.md)
* [E0039](E0039.md)
* [E0040](E0040.md)
* [E0041](E0041.md)
* [E0042](E0042.md)
* [E0043](E0043.md)
* [E0044](E0044.md)
* [E0045](E0045.md)
* [E0046](E0046.md)
* [E0047](E0047.md)
* [E0049](E0049.md)
* [E0050](E0050.md)
* [E0051](E0051.md)
* [E0052](E0052.md)
* [E0053](E0053.md)
* [E0054](E0054.md)
* [E0055](E0055.md)
* [E0056](E0056.md)
* [E0057](E0057.md)
* [E0058](E0058.md)
* [E0059](E0059.md)
* [E0060](E0060.md)
* [E0061](E0061.md)
* [E0062](E0062.md)
* [E0063](E0063.md)
* [E0064](E0064.md)
* [E0065](E0065.md)
* [E0066](E0066.md)
* [E0067](E0067.md)
* [E0068](E0068.md)
* [E0069](E0069.md)
* [E0070](E0070.md)
* [E0071](E0071.md)
* [E0072](E0072.md)
* [E0073](E0073.md)
* [E0074](E0074.md)
* [E0075](E0075.md)
* [E0076](E0076.md)
* [E0077](E0077.md)
* [E1000](E1000.md)
* [E1001](E1001.md)
* [E3001](E3001.md)
* [E3002](E3002.md)
* [E3003](E3003.md)
* [E3004](E3004.md)
* [E3005](E3005.md)
* [E3006](E3006.md)
* [E3007](E3007.md)
* [E3008](E3008.md)
* [E3009](E3009.md)
* [E3010](E3010.md)
* [E3011](E3011.md)
* [E3012](E3012.md)
* [E3014](E3014.md)
* [E3016](E3016.md)
* [E3017](E3017.md)
* [E3018](E3018.md)
* [E3019](E3019.md)
* [E3020](E3020.md)
* [E3021](E3021.md)
* [E3022](E3022.md)
* [E3023](E3023.md)
* [E3024](E3024.md)
* [E3100](E3100.md)
* [E3700](E3700.md)
* [E3800](E3800.md)
* [E3801](E3801.md)
* [E4000](E4000.md)
* [E4001](E4001.md)
* [E4002](E4002.md)
* [E4003](E4003.md)
* [E4005](E4005.md)
* [E4006](E4006.md)
* [E4008](E4008.md)
* [E4010](E4010.md)
* [E4011](E4011.md)
* [E4012](E4012.md)
* [E4013](E4013.md)
* [E4014](E4014.md)
* [E4015](E4015.md)
* [E4017](E4017.md)
* [E4018](E4018.md)
* [E4019](E4019.md)
* [E4020](E4020.md)
* [E4021](E4021.md)
* [E4022](E4022.md)
* [E4023](E4023.md)
* [E4024](E4024.md)
* [E4027](E4027.md)
* [E4028](E4028.md)
* [E4029](E4029.md)
* [E4031](E4031.md)
* [E4032](E4032.md)
* [E4033](E4033.md)
* [E4034](E4034.md)
* [E4036](E4036.md)
* [E4037](E4037.md)
* [E4038](E4038.md)
* [E4039](E4039.md)
* [E4040](E4040.md)
* [E4041](E4041.md)
* [E4042](E4042.md)
* [E4043](E4043.md)
* [E4044](E4044.md)
* [E4046](E4046.md)
* [E4047](E4047.md)
* [E4048](E4048.md)
* [E4049](E4049.md)
* [E4050](E4050.md)
* [E4051](E4051.md)
* [E4052](E4052.md)
* [E4053](E4053.md)
* [E4054](E4054.md)
* [E4055](E4055.md)
* [E4056](E4056.md)
* [E4057](E4057.md)
* [E4059](E4059.md)
* [E4061](E4061.md)
* [E4063](E4063.md)
* [E4064](E4064.md)
* [E4065](E4065.md)
* [E4066](E4066.md)
* [E4067](E4067.md)
* [E4068](E4068.md)
* [E4069](E4069.md)
* [E4070](E4070.md)
* [E4071](E4071.md)
* [E4073](E4073.md)
* [E4074](E4074.md)
* [E4077](E4077.md)
* [E4078](E4078.md)
* [E4080](E4080.md)
* [E4081](E4081.md)
* [E4082](E4082.md)
* [E4084](E4084.md)
* [E4085](E4085.md)
* [E4086](E4086.md)
* [E4087](E4087.md)
* [E4089](E4089.md)
* [E4091](E4091.md)
* [E4092](E4092.md)
* [E4093](E4093.md)
* [E4094](E4094.md)
* [E4095](E4095.md)
* [E4096](E4096.md)
* [E4099](E4099.md)
* [E4100](E4100.md)
* [E4101](E4101.md)
* [E4102](E4102.md)
* [E4104](E4104.md)
* [E4106](E4106.md)
* [E4107](E4107.md)
* [E4108](E4108.md)
* [E4109](E4109.md)
* [E4110](E4110.md)
* [E4111](E4111.md)
* [E4112](E4112.md)
* [E4113](E4113.md)
* [E4114](E4114.md)
* [E4115](E4115.md)
* [E4116](E4116.md)
* [E4117](E4117.md)
* [E4118](E4118.md)
* [E4119](E4119.md)
* [E4120](E4120.md)
* [E4121](E4121.md)
* [E4122](E4122.md)
* [E4124](E4124.md)
* [E4127](E4127.md)
* [E4128](E4128.md)
* [E4130](E4130.md)
* [E4131](E4131.md)
* [E4132](E4132.md)
* [E4133](E4133.md)
* [E4135](E4135.md)
* [E4137](E4137.md)
* [E4138](E4138.md)
* [E4139](E4139.md)
* [E4140](E4140.md)
* [E4141](E4141.md)
* [E4142](E4142.md)
* [E4143](E4143.md)
* [E4144](E4144.md)
* [E4145](E4145.md)
* [E4146](E4146.md)
* [E4147](E4147.md)
* [E4148](E4148.md)
* [E4149](E4149.md)
* [E4151](E4151.md)
* [E4153](E4153.md)
* [E4154](E4154.md)
* [E4155](E4155.md)
* [E4156](E4156.md)
* [E4157](E4157.md)
* [E4158](E4158.md)
* [E4159](E4159.md)
* [E4160](E4160.md)
* [E4161](E4161.md)
* [E4162](E4162.md)
* [E4163](E4163.md)
* [E4164](E4164.md)
* [E4165](E4165.md)
* [E4167](E4167.md)
* [E4168](E4168.md)
* [E4171](E4171.md)
* [E4172](E4172.md)
* [E4173](E4173.md)
* [E4174](E4174.md)
* [E4175](E4175.md)
* [E4176](E4176.md)
* [E4177](E4177.md)
* [E4180](E4180.md)
* [E4181](E4181.md)
* [E4182](E4182.md)
* [E4183](E4183.md)
* [E4184](E4184.md)
* [E4185](E4185.md)
* [E4186](E4186.md)
* [E4187](E4187.md)
* [E4188](E4188.md)
* [E4189](E4189.md)
* [E4190](E4190.md)
* [E4191](E4191.md)
* [E4192](E4192.md)
* [E4193](E4193.md)
* [E4194](E4194.md)
* [E4195](E4195.md)
* [E4196](E4196.md)
* [E4197](E4197.md)
* [E4198](E4198.md)
* [E4199](E4199.md)
* [E4200](E4200.md)
* [E4201](E4201.md)
* [E4202](E4202.md)
* [E4203](E4203.md)
* [E4204](E4204.md)
* [E4205](E4205.md)
* [E4206](E4206.md)
* [E4207](E4207.md)
* [E4208](E4208.md)
* [E4209](E4209.md)
* [E4210](E4210.md)
* [E4211](E4211.md)
* [E4212](E4212.md)
* [E4213](E4213.md)
* [E4214](E4214.md)
* [E4215](E4215.md)
