moonbit-agent-guide

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Agent Workflow

Agent 工作流

For fast, reliable task execution, follow this order:
  1. Clarify goal and constraints
    • Confirm expected behavior, non-goals, and compatibility constraints (target backend, public API stability, performance limits).
  2. Locate module/package boundaries
    • Find
      moon.mod.json
      (module root) and relevant
      moon.pkg
      /
      moon.pkg.json
      files (package boundaries and imports).
  3. Discover APIs before coding
    • Prefer
      moon ide doc
      queries to discover existing functions/types/methods before adding new code.
    • Use
      moon ide outline
      ,
      moon ide peek-def
      , and
      moon ide find-references
      for semantic navigation.
  4. Reliable refactoring
    • Use
      moon ide rename
      for semantic refactoring. If multiple symbols share a name, add
      --loc filename:line:col
      .
    • Use
      #deprecated
      when old APIs should warn and be removed after migration.
    • Use
      #alias(old_api, deprecated)
      when temporary backward compatibility is required during migration.
    • Remove
      #deprecated
      and
      #alias
      shims once callers are migrated and warnings are gone.
  5. Edit minimally and package-locally
    • Keep changes inside the correct package, use
      ///|
      top-level delimiters, and split code into cohesive files.
  6. Validate in a tight loop
    • Run
      moon check
      after edits.
    • Run targeted tests with
      moon test [dirname|filename] --filter 'glob'
      and use
      moon test --update
      for snapshot changes.
  7. Finalize before handoff
    • Run
      moon fmt
      .
    • Run
      moon info
      to verify whether public APIs changed (
      pkg.generated.mbti
      diff).
    • Report changed files, validation commands, and any remaining risks.
为实现快速、可靠的任务执行,请遵循以下步骤:
  1. 明确目标与约束条件
    • 确认预期行为、非目标范围以及兼容性约束(目标后端、公共 API 稳定性、性能限制)。
  2. 定位模块/包边界
    • 找到
      moon.mod.json
      (模块根目录标识)和相关的
      moon.pkg
      /
      moon.pkg.json
      文件(包边界与导入配置)。
  3. 编码前先探索 API
    • 优先使用
      moon ide doc
      命令查询现有函数/类型/方法,再考虑添加新代码。
    • 使用
      moon ide outline
      moon ide peek-def
      moon ide find-references
      进行语义导航。
  4. 可靠的重构操作
    • 使用
      moon ide rename
      进行语义重构。若多个符号同名,添加
      --loc filename:line:col
      参数指定位置。
    • 当旧 API 需要发出警告并在迁移后移除时,使用
      #deprecated
      标记。
    • 迁移期间需临时保持向后兼容时,使用
      #alias(old_api, deprecated)
    • 当所有调用方完成迁移且警告消失后,移除
      #deprecated
      #alias
      兼容代码。
  5. 最小化修改并保持包内操作
    • 将修改限制在对应包内,使用
      ///|
      顶级分隔符,将代码拆分为高内聚的文件。
  6. 循环验证修改
    • 编辑后运行
      moon check
    • 使用
      moon test [dirname|filename] --filter 'glob'
      运行针对性测试,快照变更时使用
      moon test --update
  7. 交付前的最终确认
    • 运行
      moon fmt
      格式化代码。
    • 运行
      moon info
      验证公共 API 是否变更(对比
      pkg.generated.mbti
      差异)。
    • 报告变更文件、验证命令执行情况以及剩余风险。

Fast Task Playbooks

快速任务执行手册

Use the smallest playbook that matches the request.
选择与需求匹配的最简执行流程。

Bug Fix (No API Change Intended)

Bug 修复(不涉及 API 变更)

  1. Reproduce or identify the failing behavior.
  2. Locate symbols with
    moon ide outline
    ,
    moon ide peek-def
    ,
    moon ide find-references
    .
  3. Implement minimal fix in the current package.
  4. Validate with:
    • moon check
    • moon test [dirname|filename] --filter 'glob'
      (or closest targeted test scope)
    • moon fmt
    • moon info
      (confirm
      pkg.generated.mbti
      unchanged)
  1. 复现或定位失败行为。
  2. 使用
    moon ide outline
    moon ide peek-def
    moon ide find-references
    定位相关符号。
  3. 在当前包内实现最小化修复。
  4. 执行以下验证:
    • moon check
    • moon test [dirname|filename] --filter 'glob'
      (或最接近的针对性测试范围)
    • moon fmt
    • moon info
      (确认
      pkg.generated.mbti
      无变更)

Refactor (Behavior Preserving)

重构(保持行为不变)

  1. Confirm behavior/API invariants first.
  2. Prefer semantic rename/navigation tools:
    • moon ide rename
    • moon ide find-references
    • moon ide peek-def
    • If multiple symbols share a name, use
      moon ide rename <symbol> <new_name> --loc filename:line:col
      .
  3. Keep edits package-local and file-organization-focused.
  4. Validate with:
    • moon check
    • moon test [dirname|filename]
    • moon fmt
    • moon info
      (API should remain unchanged unless requested)
  1. 先确认行为/API 不变量。
  2. 优先使用语义重命名/导航工具:
    • moon ide rename
    • moon ide find-references
    • moon ide peek-def
    • 若多个符号同名,使用
      moon ide rename <symbol> <new_name> --loc filename:line:col
  3. 保持修改在包内,聚焦文件组织优化。
  4. 执行以下验证:
    • moon check
    • moon test [dirname|filename]
    • moon fmt
    • moon info
      (除非明确要求,否则 API 应保持不变)

New Feature or Public API

新功能或公共 API 开发

  1. Discover existing idioms with
    moon ide doc
    before introducing new names.
  2. Add implementation in cohesive files with
    ///|
    delimiters.
  3. Add/extend black-box tests and docstring examples for public APIs.
  4. Validate with:
    • moon check
    • moon test [dirname|filename]
      (use
      --update
      for snapshots when needed)
    • moon fmt
    • moon info
      (review and keep intended
      pkg.generated.mbti
      changes)
  1. 先使用
    moon ide doc
    探索现有编程范式,再引入新命名。
  2. 使用
    ///|
    分隔符,在高内聚的文件中添加实现。
  3. 为公共 API 添加/扩展黑盒测试和文档字符串示例。
  4. 执行以下验证:
    • moon check
    • moon test [dirname|filename]
      (必要时使用
      --update
      更新快照)
    • moon fmt
    • moon info
      (检查并保留预期的
      pkg.generated.mbti
      变更)

MoonBit Project Layouts

MoonBit 项目布局

MoonBit uses the
.mbt
extension for source code files and interface files with the
.mbti
extension. At the top-level of a MoonBit project there is a
moon.mod.json
file specifying the metadata of the project. The project may contain multiple packages, each with its own
moon.pkg
or
moon.pkg.json
(legacy mode). Subdirectories may also contain
moon.mod.json
files indicating that a different set of dependencies can be used for that subdir.
MoonBit 使用
.mbt
作为源代码文件扩展名,
.mbti
作为接口文件扩展名。MoonBit 项目的顶层目录下有一个
moon.mod.json
文件,用于指定项目元数据。项目可包含多个包,每个包都有自己的
moon.pkg
moon.pkg.json
(旧版格式)。子目录中也可能包含
moon.mod.json
文件,表示该子目录可使用独立的依赖集。

Example layout

示例布局

my_module
├── moon.mod.json             # Module metadata, source field (optional) specifies the source directory of the module
├── moon.pkg             # Package metadata (each directory is a package like Golang)
├── README.mbt.md             # Markdown with tested code blocks (`test "..." { ... }`)
├── README.md -> README.mbt.md
├── cmd                       # Command line directory
│   └── main
│       ├── main.mbt
│       └── moon.pkg     # executable package with `options("is-main": true)`
├── liba/                     # Library packages
│   └── moon.pkg         # Referenced by other packages as `@username/my_module/liba`
│   └── libb/                 # Library packages
│       └── moon.pkg     # Referenced by other packages as `@username/my_module/liba/libb`
├── user_pkg.mbt              # Root packages, referenced by other packages as `@username/my_module`
├── user_pkg_wbtest.mbt       # White-box tests (only needed for testing internal private members, similar to Golang's package mypackage)
└── user_pkg_test.mbt         # Black-box tests
└── ...                       # More package files, symbols visible to current package (like Golang)
  • Module: characterized by a
    moon.mod.json
    file in the project root directory. A MoonBit module is like a Go module; it is a collection of packages in subdirectories, usually corresponding to a repository or project. Module boundaries matter for dependency management and import paths.
  • Package: characterized by a
    moon.pkg
    (or
    moon.pkg.json
    ) file in each directory. All subcommands of
    moon
    will still be executed in the directory of the module (where
    moon.mod.json
    is located), not the current package. A MoonBit package is the actual compilation unit (like a Go package). All source files in the same package are concatenated into one unit and thereby share all definitions throughout that package. The
    name
    in the
    moon.mod.json
    file combined with the relative path to the package source directory defines the package name, not the file name. Imports refer to module + package paths, NEVER to file names.
  • Files: A
    .mbt
    file is just a chunk of source code inside a package. File names do NOT create modules, packages, or namespaces. You may freely split/merge/move declarations between files in the same package. Any declaration in a package can reference any other declaration in that package, regardless of file.
my_module
├── moon.mod.json             # 模块元数据,source 字段(可选)指定模块的源目录
├── moon.pkg             # 包元数据(每个目录对应一个包,类似 Golang)
├── README.mbt.md             # 包含可测试代码块的 Markdown 文件(`test "..." { ... }`)
├── README.md -> README.mbt.md
├── cmd                       # 命令行工具目录
│   └── main
│       ├── main.mbt
│       └── moon.pkg     # 可执行包,需配置 `options("is-main": true)`
├── liba/                     # 库包
│   └── moon.pkg         # 其他包可通过 `@username/my_module/liba` 引用
│   └── libb/                 # 库包
│       └── moon.pkg     # 其他包可通过 `@username/my_module/liba/libb` 引用
├── user_pkg.mbt              # 根包,其他包可通过 `@username/my_module` 引用
├── user_pkg_wbtest.mbt       # 白盒测试文件(仅用于测试内部私有成员,类似 Golang 的 package mypackage)
└── user_pkg_test.mbt         # 黑盒测试文件
└── ...                       # 更多包文件,当前包内所有符号可见(类似 Golang)
  • 模块:由项目根目录下的
    moon.mod.json
    文件标识。MoonBit 模块类似 Go 模块,是子目录中多个包的集合,通常对应一个仓库或项目。模块边界对依赖管理和导入路径至关重要。
  • :由每个目录下的
    moon.pkg
    (或
    moon.pkg.json
    )文件标识。所有
    moon
    子命令仍在模块目录(
    moon.mod.json
    所在目录)中执行,而非当前包目录。MoonBit 包是实际的编译单元(类似 Go 包)。同一包内的所有源文件会被合并为一个单元,包内所有定义可互相访问。包名由
    moon.mod.json
    中的
    name
    字段加上包源目录的相对路径组成,与文件名无关。导入时需引用「模块+包路径」,绝对不能引用文件名。
  • 文件
    .mbt
    文件只是包内的一段源代码。文件名不会定义模块、包或命名空间。可自由地在同一包内的文件之间拆分/合并/移动声明。同一包内的任何声明均可引用包内其他声明,与所在文件无关。

Coding/layout rules you MUST follow:

必须遵循的编码/布局规则:

  1. Prefer many small, cohesive files over one large file.
    • Group related types and functions into focused files (e.g. http_client.mbt, router.mbt).
    • If a file is getting large or unfocused, create a new file and move related declarations into it.
  2. You MAY freely move declarations between files inside the same package.
    • Each block is separated by
      ///|
      . Moving a function/struct/trait between files does not change semantics, as long as its name and pub-ness stay the same. The order of each block is irrelevant too.
    • It is safe to refactor by splitting or merging files inside a package.
  3. File names are purely organizational.
    • Do NOT assume file names define modules, and do NOT use file names in type paths.
    • Choose file names to describe a feature or responsibility, not to mirror type names rigidly.
  4. When adding new code:
    • Prefer adding it to an existing file that matches the feature.
    • If no good file exists, create a new file under the same package with a descriptive name.
    • Avoid creating giant "impl", “misc”, or “util” files.
  5. Tests:
    • Place tests in dedicated test files (e.g.
      *_test.mbt
      ) within the appropriate package. For a package (besides
      *_test.mbt
      files),
      *.mbt.md
      files are also blackbox test files in addition to Markdown files. The code blocks (separated by triple backticks)
      mbt check
      are treated as test cases and serve both purposes: documentation and tests. You may have
      README.mbt.md
      files with
      mbt check
      code examples. You can also symlink
      README.mbt.md
      to
      README.md
      to make it integrate better with GitHub.
    • It is fine — and encouraged — to have multiple small test files.
  6. Interface files (
    pkg.generated.mbti
    )
    pkg.generated.mbti
    files are compiler-generated summaries of each package's public API surface. They provide a formal, concise overview of all exported types, functions, and traits without implementation details. They are generated using
    moon info
    and useful for code review. When you have a commit that does not change public APIs,
    pkg.generated.mbti
    files will remain unchanged, so it is recommended to put
    pkg.generated.mbti
    in version control when you are done.
    For IDE navigation and symbol lookup commands, see the dedicated
    moon ide
    section below.
  1. 优先使用多个小而内聚的文件,而非单个大文件。
    • 将相关类型和函数分组到聚焦的文件中(例如 http_client.mbt、router.mbt)。
    • 若文件过大或关注点分散,创建新文件并将相关声明移入。
  2. 可自由地在同一包内的文件之间移动声明。
    • 每个代码块由
      ///|
      分隔。只要函数/结构体/ trait 的名称和可见性不变,在文件间移动不会改变语义,代码块的顺序也无关紧要。
    • 在包内拆分或合并文件是安全的重构操作。
  3. 文件名仅用于组织代码。
    • 不要假设文件名定义了模块,也不要在类型路径中使用文件名。
    • 选择文件名时应描述功能或职责,而非严格镜像类型名称。
  4. 添加新代码时:
    • 优先添加到匹配该功能的现有文件中。
    • 若没有合适的文件,在同一包下创建一个描述性名称的新文件。
    • 避免创建庞大的「impl」、「misc」或「util」文件。
  5. 测试:
    • 将测试放在对应包内的专用测试文件中(例如
      *_test.mbt
      )。对于包(除
      *_test.mbt
      文件外),
      *.mbt.md
      文件既是 Markdown 文档,也是黑盒测试文件。其中用三个反引号分隔的
      mbt check
      代码块会被视为测试用例,同时兼具文档和测试功能。可创建包含
      mbt check
      代码示例的
      README.mbt.md
      文件,也可将
      README.mbt.md
      软链接到
      README.md
      ,以更好地适配 GitHub。
    • 鼓励使用多个小型测试文件。
  6. 接口文件(
    pkg.generated.mbti
    pkg.generated.mbti
    文件是编译器生成的每个包的公共 API 摘要。它提供了所有导出类型、函数和 trait 的正式、简洁概述,不含实现细节。可通过
    moon info
    命令生成,用于代码评审。当提交不涉及公共 API 变更时,
    pkg.generated.mbti
    文件不会改变,因此建议在完成开发后将其纳入版本控制。
    有关 IDE 导航和符号查找命令,请参阅下文专门的
    moon ide
    部分。

Common Pitfalls to Avoid

需避免的常见陷阱

  • Don't use uppercase for variables/functions - compilation error
  • Don't forget
    mut
    for mutable record fields
    - immutable by default (note that Arrays typically do NOT need
    mut
    unless completely reassigning to the variable - simple push operations, for example, do not need
    mut
    )
  • Don't ignore error handling - errors must be explicitly handled
  • Don't use
    return
    unnecessarily
    - the last expression is the return value
  • Don't create methods without Type:: prefix - methods need explicit type prefix
  • Don't forget to handle array bounds - use
    get()
    for safe access
  • Don't forget @package prefix when calling functions from other packages
  • Don't use ++ or -- (not supported) - use
    i = i + 1
    or
    i += 1
  • Don't add explicit
    try
    for error-raising functions
    - errors propagate automatically (unlike Swift)
  • Legacy syntax: Older code may use
    function_name!(...)
    or
    function_name(...)?
    - these are deprecated; use normal calls and
    try?
    for Result conversion
  • Prefer range
    for
    loops over C-style
    -
    for i in 0..<(n-1) {...}
    and
    for j in 0..=6 {...}
    are more idiomatic in MoonBit
  • Async - MoonBit has no
    await
    keyword; do not add it. Async functions and tests are characterized by those which call other async functions. To identify a function or test as async, simply add the
    async
    prefix (e.g.
    [pub] async fn ...
    ,
    async test ...
    ).
  • 变量/函数名不要使用大写 - 会导致编译错误
  • 可变记录字段不要忘记加
    mut
    - 默认不可变(注意:数组通常不需要
    mut
    ,除非完全重新赋值变量,例如简单的 push 操作不需要
    mut
  • 不要忽略错误处理 - 必须显式处理错误
  • 不要不必要地使用
    return
    - 最后一个表达式即为返回值
  • 方法定义不要省略 Type:: 前缀 - 方法需要显式的类型前缀
  • 不要忘记处理数组边界 - 使用
    get()
    进行安全访问
  • 调用其他包的函数时不要忘记加 @package 前缀
  • 不要使用 ++ 或 --(不支持) - 使用
    i = i + 1
    i += 1
  • 不要为会抛出错误的函数添加显式
    try
    - 错误会自动传播(与 Swift 不同)
  • 旧版语法:旧代码可能使用
    function_name!(...)
    function_name(...)?
    - 这些已被弃用;请使用普通调用和
    try?
    进行 Result 转换
  • 优先使用范围
    for
    循环而非 C 风格循环
    -
    for i in 0..<(n-1) {...}
    for j in 0..=6 {...}
    是 MoonBit 中更符合习惯的写法
  • 异步 - MoonBit 没有
    await
    关键字;不要添加该关键字。调用其他异步函数的函数和测试即为异步函数/测试。只需添加
    async
    前缀即可将函数或测试标记为异步(例如
    [pub] async fn ...
    async test ...
    )。

moon
Essentials

moon
核心命令

Essential Commands

基础命令

  • moon new my_project
    - Create new project
  • moon run cmd/main
    - Run main package
  • moon build
    - Build project (
    moon run
    and
    moon build
    both support
    --target
    )
  • moon check
    - Type check without building, use it REGULARLY, it is fast (
    moon check
    also supports
    --target
    )
  • moon info
    - Type check and generate
    mbti
    files. Run it to see if any public interfaces changed. (
    moon info
    also supports
    --target
    .)
  • moon check --target all
    - Type check for all backends
  • moon add package
    - Add dependency
  • moon remove package
    - Remove dependency
  • moon fmt
    - Format code - should be run periodically - note that the files may be rewritten Note you can also use
    moon -C dir check
    to run commands in a specific directory.
  • moon new my_project
    - 创建新项目
  • moon run cmd/main
    - 运行主包
  • moon build
    - 构建项目 (
    moon run
    moon build
    均支持
    --target
    参数)
  • moon check
    - 仅进行类型检查,不构建,建议定期使用,速度很快 (
    moon check
    也支持
    --target
    参数)
  • moon info
    - 进行类型检查并生成
    mbti
    文件。运行该命令可查看公共接口是否变更。 (
    moon info
    也支持
    --target
    参数。)
  • moon check --target all
    - 为所有后端进行类型检查
  • moon add package
    - 添加依赖
  • moon remove package
    - 移除依赖
  • moon fmt
    - 格式化代码 - 应定期运行 - 注意文件可能会被重写 注意:也可使用
    moon -C dir check
    在指定目录中运行命令。

Test Commands

测试命令

  • moon test
    - Run all tests (
    moon test
    also supports
    --target
    )
  • moon test --update
    - Update snapshots
  • moon test -v
    - Verbose output with test names
  • moon test [dirname|filename]
    - Test specific directory or file
  • moon coverage analyze
    - Analyze coverage
  • moon test [dirname|filename] --filter 'glob'
    - Run tests matching filter
    moon test float/float_test.mbt --filter "Float::*"
    moon test float -F "Float::*" // shortcut syntax
  • moon test
    - 运行所有测试 (
    moon test
    也支持
    --target
    参数)
  • moon test --update
    - 更新快照
  • moon test -v
    - 显示详细测试名称
  • moon test [dirname|filename]
    - 测试指定目录或文件
  • moon coverage analyze
    - 分析测试覆盖率
  • moon test [dirname|filename] --filter 'glob'
    - 运行匹配过滤规则的测试
    moon test float/float_test.mbt --filter "Float::*"
    moon test float -F "Float::*" // 简写语法

README.mbt.md
Generation Guide

README.mbt.md
生成指南

  • Output
    README.mbt.md
    in the package directory.
    *.mbt.md
    file and docstring contents treats
    mbt check
    specially.
    mbt check
    block will be included directly as code and also run by
    moon check
    and
    moon test
    . If you don't want the code snippets to be checked, explicit
    mbt nocheck
    is preferred. If you are only referencing types from the package, you should use
    mbt nocheck
    which will only be syntax highlighted. Symlink
    README.mbt.md
    to
    README.md
    to adapt to systems that expect
    README.md
    .
  • 在包目录中生成
    README.mbt.md
    文件。
    *.mbt.md
    文件和文档字符串内容会特殊处理
    mbt check
    标记。
    mbt check
    块会直接作为代码包含,同时会被
    moon check
    moon test
    执行。若不希望代码片段被检查,建议显式使用
    mbt nocheck
    。若仅引用包内的类型,应使用
    mbt nocheck
    ,这样仅会进行语法高亮。将
    README.mbt.md
    软链接到
    README.md
    ,以适配期望
    README.md
    的系统。

Testing Guide

测试指南

Use snapshot tests as it is easy to update when behavior changes.
  • Snapshot Tests:
    inspect(value, content="...")
    . If unknown, write
    inspect(value)
    and run
    moon test --update
    (or
    moon test -u
    ).
    • Use regular
      inspect()
      for simple values (uses
      Show
      trait)
    • Use
      @json.inspect()
      for complex nested structures (uses
      ToJson
      trait, produces more readable output)
    • It is encouraged to
      inspect
      or
      @json.inspect
      the whole return value of a function if the whole return value is not huge, this makes the test simple. You need
      impl (Show|ToJson) for YourType
      or
      derive (Show, ToJson)
      .
  • Update workflow: After changing code that affects output, run
    moon test --update
    to regenerate snapshots, then review the diffs in your test files (the
    content=
    parameter will be updated automatically).
  • Validation order: Follow the canonical sequence in
    Agent Workflow
    and
    Fast Task Playbooks
    .
  • Black-box by default: Call only public APIs via
    @package.fn
    . Use white-box tests only when private members matter.
  • Grouping: Combine related checks in one
    test "..." { ... }
    block for speed and clarity.
  • Panics: Name tests with prefix
    test "panic ..." {...}
    ; if the call returns a value, wrap it with
    ignore(...)
    to silence warnings.
  • Errors: Use
    try? f()
    to get
    Result[...]
    and
    inspect
    it when a function may raise.
优先使用快照测试,当行为变更时易于更新。
  • 快照测试
    inspect(value, content="...")
    。若未知预期结果,可先写
    inspect(value)
    ,再运行
    moon test --update
    (或
    moon test -u
    )生成快照。
    • 简单值使用常规
      inspect()
      (依赖
      Show
      trait)
    • 复杂嵌套结构使用
      @json.inspect()
      (依赖
      ToJson
      trait,生成更易读的输出)
    • 若函数的返回值不是特别大,建议
      inspect
      @json.inspect
      整个返回值,这样测试会更简单。需要为自定义类型实现
      (Show|ToJson) for YourType
      derive (Show, ToJson)
  • 更新流程:修改影响输出的代码后,运行
    moon test --update
    重新生成快照,然后评审测试文件中的差异(
    content=
    参数会自动更新)。
  • 验证顺序:遵循「Agent 工作流」和「快速任务执行手册」中的标准流程。
  • 默认使用黑盒测试:仅通过
    @package.fn
    调用公共 API。仅当私有成员相关测试必要时才使用白盒测试。
  • 分组测试:将相关检查合并到一个
    test "..." { ... }
    块中,以提高速度和清晰度。
  • 恐慌测试:测试名称以
    test "panic ..." {...}
    为前缀;若调用返回值,用
    ignore(...)
    包裹以消除警告。
  • 错误处理:使用
    try? f()
    获取
    Result[...]
    inspect
    结果,适用于可能抛出错误的函数。

Docstring tests

文档字符串测试

Public APIs are encouraged to have docstring tests.
mbt
///|
/// Get the largest element of a non-empty `Array`.
///
/// # Example
/// ```mbt check
/// test {
///   inspect(sum_array([1, 2, 3, 4, 5, 6]), content="21")
/// }
/// ```
///
/// # Panics
/// Panics if the `xs` is empty.
pub fn sum_array(xs : Array[Int]) -> Int {
  xs.fold(init=0, (a, b) => a + b)
}
The MoonBit code in a docstring will be type checked and tested automatically (using
moon test --update
). In docstrings,
mbt check
should only contain
test
or
async test
.
鼓励为公共 API 添加文档字符串测试。
mbt
///|
/// 获取非空 `Array` 中的最大元素。
///
/// # 示例
/// ```mbt check
/// test {
///   inspect(sum_array([1, 2, 3, 4, 5, 6]), content="21")
/// }
/// ```
///
/// # 恐慌场景
/// 当 `xs` 为空时会触发恐慌。
pub fn sum_array(xs : Array[Int]) -> Int {
  xs.fold(init=0, (a, b) => a + b)
}
文档字符串中的 MoonBit 代码会被自动进行类型检查和测试(通过
moon test --update
)。在文档字符串中,
mbt check
块应仅包含
test
async test

Spec-driven Development

基于规格的开发

  • The spec can be written in a readonly
    spec.mbt
    file (name is conventional, not mandatory) with stub code marked as declarations:
mbt
///|
declare pub type Yaml

///|
declare pub fn Yaml::to_string(y : Yaml) -> String raise

///|
declare pub impl Eq for Yaml

///|
declare pub fn parse_yaml(s : String) -> Yaml raise
  • Add
    spec_easy_test.mbt
    ,
    spec_difficult_test.mbt
    , etc. to test the spec functions; everything will be type-checked(
    moon check
    ).
  • The AI or users can implement the
    declare
    functions in different files thanks to our package organization.
  • Run
    moon test
    to check everything is correct.
  • declare
    is supported for functions, methods, and types.
  • The
    pub type Yaml
    line is an intentionally opaque placeholder; the implementer chooses its representation.
  • Note the spec file can also contain normal code, not just declarations.
  • 规格可写在只读的
    spec.mbt
    文件中(名称是约定俗成的,非强制),其中存根代码用
    declare
    标记:
mbt
///|
declare pub type Yaml

///|
declare pub fn Yaml::to_string(y : Yaml) -> String raise

///|
declare pub impl Eq for Yaml

///|
declare pub fn parse_yaml(s : String) -> Yaml raise
  • 添加
    spec_easy_test.mbt
    spec_difficult_test.mbt
    等文件测试规格函数;所有内容会被
    moon check
    进行类型检查。
  • 借助包组织方式,AI 或用户可在不同文件中实现
    declare
    标记的函数。
  • 运行
    moon test
    验证所有内容是否正确。
  • declare
    支持函数、方法和类型。
  • pub type Yaml
    是一个故意模糊的占位符,实现者可选择其具体表示。
  • 注意:规格文件也可包含常规代码,不仅限于声明。

moon ide [doc|peek-def|outline|find-references|hover|rename]
for code navigation and refactoring

moon ide [doc|peek-def|outline|find-references|hover|rename]
用于代码导航和重构

For project-local symbols and navigation, use:
  • moon ide doc <query>
    to discover available APIs, functions, types, and methods in MoonBit. Always prefer
    moon ide doc
    over other approaches when exploring what APIs are available, it is more powerful and accurate than
    grep_search
    or any regex-based searching tools.
  • moon ide outline .
    to scan a package,
  • moon ide find-references <symbol>
    to locate usages, and
  • moon ide peek-def
    for inline definition context and to locate toplevel symbols.
  • moon ide hover sym --loc filename:line:col
    to get type information at a specific location.
  • moon ide rename <symbol> <new_name> [--loc filename:line:col]
    to rename a symbol project-wide. Prefer
    --loc
    when symbol names are ambiguous. These tools save tokens and are more precise than grepping (
    grep
    displays results in both definitions and call sites including comments too).
对于项目内的符号和导航,使用以下命令:
  • moon ide doc <query>
    探索 MoonBit 中可用的 API、函数、类型和方法。探索可用 API 时,始终优先使用
    moon ide doc
    ,它比
    grep_search
    或任何基于正则的搜索工具更强大、更准确
  • moon ide outline .
    扫描包内符号,
  • moon ide find-references <symbol>
    定位符号用法,
  • moon ide peek-def
    查看内联定义上下文并定位顶级符号。
  • moon ide hover sym --loc filename:line:col
    获取指定位置的类型信息。
  • moon ide rename <symbol> <new_name> [--loc filename:line:col]
    在项目范围内重命名符号。当符号名称存在歧义时,优先使用
    --loc
    参数。这些工具比 grep 更节省时间且更精确(grep 会同时显示定义、调用位置和注释中的结果)。

moon ide doc
for API Discovery

moon ide doc
用于 API 探索

moon ide doc
uses a specialized query syntax designed for symbol lookup:
  • Empty query:
    moon ide doc ''
    • In a module: shows all available packages in current module, including dependencies and moonbitlang/core
    • In a package: shows all symbols in current package
    • Outside package: shows all available packages
  • Function/value lookup:
    moon ide doc "[@pkg.]value_or_function_name"
  • Type lookup:
    moon ide doc "[@pkg.]Type_name"
    (builtin type does not need package prefix)
  • Method/field lookup:
    moon ide doc "[@pkg.]Type_name::method_or_field_name"
  • Package exploration:
    moon ide doc "@pkg"
    • Show package
      pkg
      and list all its exported symbols
    • Example:
      moon ide doc "@json"
      - explore entire
      @json
      package
    • Example:
      moon ide doc "@encoding/utf8"
      - explore nested package
  • Globbing: Use
    *
    wildcard for partial matches, e.g.
    moon ide doc "String::*rev*"
    to find all String methods with "rev" in their name
moon ide doc
使用专门的查询语法进行符号查找:
  • 空查询
    moon ide doc ''
    • 在模块中:显示当前模块的所有可用包,包括依赖和 moonbitlang/core
    • 在包中:显示当前包的所有符号
    • 在包外:显示所有可用包
  • 函数/值查找
    moon ide doc "[@pkg.]value_or_function_name"
  • 类型查找
    moon ide doc "[@pkg.]Type_name"
    (内置类型无需包前缀)
  • 方法/字段查找
    moon ide doc "[@pkg.]Type_name::method_or_field_name"
  • 包探索
    moon ide doc "@pkg"
    • 显示包
      pkg
      并列出其所有导出符号
    • 示例:
      moon ide doc "@json"
      - 探索整个
      @json
    • 示例:
      moon ide doc "@encoding/utf8"
      - 探索嵌套包
  • 通配符匹配:使用
    *
    通配符进行部分匹配,例如
    moon ide doc "String::*rev*"
    查找所有名称包含 "rev" 的 String 方法

moon ide doc
Examples

moon ide doc
示例

bash
undefined
bash
undefined

search for String methods in standard library:

搜索标准库中的 String 方法:

$ moon ide doc "String"
type String
pub fn String::add(String, String) -> String

... more methods omitted ...

$ moon ide doc "@buffer" # list all symbols in package buffer: moonbitlang/core/buffer
fn from_array(ArrayView[Byte]) -> Buffer
$ moon ide doc "String"
type String
pub fn String::add(String, String) -> String

... 省略更多方法 ...

$ moon ide doc "@buffer" # 列出 buffer 包中的所有符号: moonbitlang/core/buffer
fn from_array(ArrayView[Byte]) -> Buffer

... omitted ...

... 省略 ...

$ moon ide doc "@buffer.new" # list the specific function in a package: package "moonbitlang/core/buffer"
pub fn new(size_hint? : Int) -> Buffer Creates ... omitted ...
$ moon ide doc "String::rev" # globbing package "moonbitlang/core/string"
pub fn String::rev(String) -> String Returns ... omitted ...

... more

pub fn String::rev_find(String, StringView) -> Int? Returns ... omitted ...

**Best practice**: Treat this section as command reference; execution order is defined in `Agent Workflow`.
$ moon ide doc "@buffer.new" # 列出包中的特定函数: package "moonbitlang/core/buffer"
pub fn new(size_hint? : Int) -> Buffer Creates ... 省略 ...
$ moon ide doc "String::rev" # 通配符匹配 package "moonbitlang/core/string"
pub fn String::rev(String) -> String Returns ... 省略 ...

... 更多

pub fn String::rev_find(String, StringView) -> Int? Returns ... 省略 ...

**最佳实践**:将本节作为命令参考;执行顺序遵循「Agent 工作流」中的定义。

moon ide rename sym new_name [--loc filename:line:col]
example

moon ide rename sym new_name [--loc filename:line:col]
示例

When the user asks: "Can you rename the function
compute_sum
to
calculate_sum
?"
$ moon ide rename compute_sum calculate_sum --loc math_utils.mbt:2

*** Begin Patch
*** Update File: cmd/main/main.mbt
@@
 ///|
 fn main {
-  println(@math_utils.compute_sum(1, 2))
+  println(@math_utils.calculate_sum(1, 2))
 }
*** Update File: math_utils.mbt
@@
 ///|
-pub fn compute_sum(a: Int, b: Int) -> Int {
+pub fn calculate_sum(a: Int, b: Int) -> Int {
   a + b
 }
*** Update File: math_utils_test.mbt
@@
 ///|
 test {
-  inspect(@math_utils.compute_sum(1, 2))
+  inspect(@math_utils.calculate_sum(1, 2))
 }
*** End Patch
当用户要求:"能否将函数
compute_sum
重命名为
calculate_sum
?"
$ moon ide rename compute_sum calculate_sum --loc math_utils.mbt:2

*** Begin Patch
*** Update File: cmd/main/main.mbt
@@
 ///|
 fn main {
-  println(@math_utils.compute_sum(1, 2))
+  println(@math_utils.calculate_sum(1, 2))
 }
*** Update File: math_utils.mbt
@@
 ///|
-pub fn compute_sum(a: Int, b: Int) -> Int {
+pub fn calculate_sum(a: Int, b: Int) -> Int {
   a + b
 }
*** Update File: math_utils_test.mbt
@@
 ///|
 test {
-  inspect(@math_utils.compute_sum(1, 2))
+  inspect(@math_utils.calculate_sum(1, 2))
 }
*** End Patch

moon ide hover sym --loc filename:line:col
example

moon ide hover sym --loc filename:line:col
示例

When the user asks: "What is the signature and docstring of
filter
? at line 14 of hover.mbt"
$ moon ide hover  filter --loc hover.mbt:14
test {
  let a: Array[Int] = [1]
  inspect(a.filter((x) => {x > 1}))
            ^^^^^^
            ```moonbit
            fn[T] Array::filter(self : Array[T], f : (T) -> Bool raise?) -> Array[T] raise?
            ```
            ---

             Creates a new array containing all elements from the input array that satisfy
             ... omitted ...
}
当用户询问:"hover.mbt 文件第14行的
filter
函数签名和文档字符串是什么?"
$ moon ide hover  filter --loc hover.mbt:14
test {
  let a: Array[Int] = [1]
  inspect(a.filter((x) => {x > 1}))
            ^^^^^^
            ```moonbit
            fn[T] Array::filter(self : Array[T], f : (T) -> Bool raise?) -> Array[T] raise?
            ```
            ---

             创建一个新数组,包含输入数组中所有满足条件的元素
             ... 省略 ...
}

moon ide peek-def sym [--loc filename:line:col]
example

moon ide peek-def sym [--loc filename:line:col]
示例

When the user asks: "Can you check if
Parser::read_u32_leb128
is implemented correctly?" you can run
moon ide peek-def Parser::read_u32_leb128
to get the definition context (this is better than
grep
since it searches the whole project by semantics):
file
L45:|///|
L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
L47:|  ...
...:| }
Now if you want to see the definition of the
Parser
struct, you can run:
bash
$ moon ide peek-def Parser --loc src/parse.mbt:46:4
Definition found at file src/parse.mbt
  | ///|
2 | priv struct Parser {
  |             ^^^^^^
  |   bytes : Bytes
  |   mut pos : Int
  | }
  |
For the
--loc
argument, the line number must be precise; the column can be approximate since the positional argument
Parser
helps locate the position.
If the "sym" is a toplevel symbol, the location can be omitted:
bash
$ moon ide peek-def String::rev
Found 1 symbols matching 'String::rev':

`pub fn String::rev` in package moonbitlang/core/builtin at /Users/usrname/.moon/lib/core/builtin/string_methods.mbt:1039-1044
1039 | ///|
     | /// Returns a new string with the characters in reverse order. It respects
     | /// Unicode characters and surrogate pairs but not grapheme clusters.
     | pub fn String::rev(self : String) -> String {
     |   self[:].rev()
     | }
当用户询问:"能否检查
Parser::read_u32_leb128
的实现是否正确?" 可运行
moon ide peek-def Parser::read_u32_leb128
获取定义上下文(这比 grep 更好,因为它会通过语义搜索整个项目):
file
L45:|///|
L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
L47:|  ...
...:| }
现在若想查看
Parser
结构体的定义,可运行:
bash
$ moon ide peek-def Parser --loc src/parse.mbt:46:4
Definition found at file src/parse.mbt
  | ///|
2 | priv struct Parser {
  |             ^^^^^^
  |   bytes : Bytes
  |   mut pos : Int
  | }
  |
对于
--loc
参数,行号必须精确;列号可近似,因为位置参数
Parser
有助于定位。
若 "sym" 是顶级符号,可省略位置参数:
bash
$ moon ide peek-def String::rev
Found 1 symbols matching 'String::rev':

`pub fn String::rev` in package moonbitlang/core/builtin at /Users/usrname/.moon/lib/core/builtin/string_methods.mbt:1039-1044
1039 | ///|
     | /// Returns a new string with the characters in reverse order. It respects
     | /// Unicode characters and surrogate pairs but not grapheme clusters.
     | pub fn String::rev(self : String) -> String {
     |   self[:].rev()
     | }

moon ide outline [dir|file]
and
moon ide find-references <sym>
for Package Symbols

moon ide outline [dir|file]
moon ide find-references <sym>
用于包符号

Use
moon ide outline
to scan a package or file for top-level symbols and locate usages without grepping.
  • moon ide outline dir
    outlines the current package directory (per-file headers)
  • moon ide outline parser.mbt
    outlines a single file This is useful when you need a quick inventory of a package, or to find the right file before
    goto-definition
    .
  • moon ide find-references TranslationUnit
    finds all references to a symbol in the current module
bash
$ moon ide outline .
spec.mbt:
 L003 | pub(all) enum CStandard {
        ...
 L013 | pub(all) struct Position {
        ...
bash
$ moon ide find-references TranslationUnit
使用
moon ide outline
扫描包或文件中的顶级符号,无需 grep 即可定位用法。
  • moon ide outline dir
    列出当前包目录的符号(按文件分组)
  • moon ide outline parser.mbt
    列出单个文件的符号 当需要快速了解包的结构,或在跳转定义前找到正确文件时,这非常有用。
  • moon ide find-references TranslationUnit
    查找当前模块中符号的所有引用
bash
$ moon ide outline .
spec.mbt:
 L003 | pub(all) enum CStandard {
        ...
 L013 | pub(all) struct Position {
        ...
bash
$ moon ide find-references TranslationUnit

Package Management

包管理

Adding Dependencies

添加依赖

sh
moon add moonbitlang/x        # Add latest version
moon add moonbitlang/x@0.4.6  # Add specific version
sh
moon add moonbitlang/x        # 添加最新版本
moon add moonbitlang/x@0.4.6  # 添加指定版本

Updating Dependencies

更新依赖

sh
moon update                   # Update package index
sh
moon update                   # 更新包索引

Typical Module configurations (
moon.mod.json
)

典型模块配置(
moon.mod.json

json
{
  "name": "username/hello", // Required format for published modules
  "version": "0.1.0",
  "source": ".", // Source directory(optional, default: ".")
  "repository": "", // Git repository URL
  "keywords": [], // Search keywords
  "description": "...", // Module description
  "deps": {
    // Dependencies from mooncakes.io, using`moon add` to add dependencies
    "moonbitlang/x": "0.4.6"
  }
}
json
{
  "name": "username/hello", // 发布模块的必填格式
  "version": "0.1.0",
  "source": ".", // 源目录(可选,默认值:".")
  "repository": "", // Git 仓库 URL
  "keywords": [], // 搜索关键词
  "description": "...", // 模块描述
  "deps": {
    // 来自 mooncakes.io 的依赖,使用`moon add`添加
    "moonbitlang/x": "0.4.6"
  }
}

Typical Package configuration (
moon.pkg
)

典型包配置(
moon.pkg

moon.pkg for simplicity
import {
  "username/hello/liba",
  "moonbitlang/x/encoding" @libb
}
import {...} for "test"
import {...} for "wbtest"
options("is-main" : true) // other options
or moon.pkg.json (legacy mode)
json
{
  "is_main": true,                 // Creates executable when true
  "import": [                      // Package dependencies
    "username/hello/liba",         // Simple import, use @liba.foo() to call functions
    {
      "path": "moonbitlang/x/encoding",
      "alias": "libb"              // Custom alias, use @libb.encode() to call functions
    }
  ],
  "test-import": [...],            // Imports for black-box tests, similar to import
  "wbtest-import": [...]           // Imports for white-box tests, similar to import (rarely used)
}
Packages are per directory and packages without a
moon.pkg
or
moon.pkg.json
file are not recognized.
简洁版 moon.pkg
import {
  "username/hello/liba",
  "moonbitlang/x/encoding" @libb
}
import {...} for "test"
import {...} for "wbtest"
options("is-main" : true) // 其他配置选项
或旧版格式 moon.pkg.json
json
{
  "is_main": true,                 // 设为 true 时生成可执行文件
  "import": [                      // 包依赖
    "username/hello/liba",         // 简单导入,使用 @liba.foo() 调用函数
    {
      "path": "moonbitlang/x/encoding",
      "alias": "libb"              // 自定义别名,使用 @libb.encode() 调用函数
    }
  ],
  "test-import": [...],            // 黑盒测试的导入,与 import 类似
  "wbtest-import": [...]           // 白盒测试的导入,与 import 类似(很少使用)
}
每个目录对应一个包,没有
moon.pkg
moon.pkg.json
文件的目录不会被识别为包。

Package Importing (used in moon.pkg)

包导入(用于 moon.pkg)

  • Import format:
    "module_name/package_path"
  • Usage:
    @alias.function()
    to call imported functions
  • Default alias: Last part of path (e.g.,
    liba
    for
    username/hello/liba
    )
  • Package reference: Use
    @packagename
    in test files to reference the tested package
Package Alias Rules:
  • Import
    "username/hello/liba"
    → use
    @liba.function()
    (default alias is the last path segment)
  • Import with custom alias
    import { "moonbitlang/x/encoding" @enc}
    → use
    @enc.function()
    (Note that this is unnecessary when the last path segment is identical to the alias name.)
  • In
    _test.mbt
    or
    _wbtest.mbt
    files, the package being tested is auto-imported
Example:
mbt
///|
/// In main.mbt after importing "username/hello/liba" in `moon.pkg`
fn main {
  println(@liba.hello()) // Calls hello() from liba package
}
  • 导入格式
    "module_name/package_path"
  • 用法:使用
    @alias.function()
    调用导入的函数
  • 默认别名:路径的最后一段(例如
    username/hello/liba
    的默认别名是
    liba
  • 包引用:在测试文件中使用
    @packagename
    引用被测试的包
包别名规则
  • 导入
    "username/hello/liba"
    → 使用
    @liba.function()
    (默认别名是路径的最后一段)
  • 使用自定义别名导入
    import { "moonbitlang/x/encoding" @enc}
    → 使用
    @enc.function()
    (当路径最后一段与别名相同时,无需自定义别名)
  • _test.mbt
    _wbtest.mbt
    文件中,被测试的包会被自动导入
示例:
mbt
///|
/// 在 `moon.pkg` 中导入 "username/hello/liba" 后,在 main.mbt 中使用
fn main {
  println(@liba.hello()) // 调用 liba 包中的 hello() 函数
}

Using the Standard Library (moonbitlang/core)

使用标准库(moonbitlang/core)

MoonBit standard library (moonbitlang/core) packages were automatically imported. MoonBit is transitioning to explicit imports—you will see a warning to add imports like
moonbitlang/core/strconv
to
moon.pkg
if you use them. The module is always available without adding to dependencies.
MoonBit 标准库(moonbitlang/core)包会被自动导入。MoonBit 正过渡到显式导入——若使用
moonbitlang/core/strconv
等包,会收到警告提示将其添加到
moon.pkg
中。标准库模块无需添加到依赖中,始终可用。

Creating Packages

创建包

To add a new package
fib
under
.
:
  1. Create directory:
    ./fib/
  2. Add
    ./fib/moon.pkg
  3. Add
    .mbt
    files with your code
  4. Import in dependent packages:
      import {
       "username/hello/fib",
      }
For more advanced topics like
conditional compilation
,
link configuration
,
warning control
, and
pre-build commands
, see
references/advanced-moonbit-build.md
.
要在当前目录下添加新包
fib
  1. 创建目录:
    ./fib/
  2. 添加
    ./fib/moon.pkg
    文件
  3. 添加
    .mbt
    代码文件
  4. 在依赖包中导入:
      import {
       "username/hello/fib",
      }
有关
条件编译
链接配置
警告控制
预构建命令
等高级主题,请参阅
references/advanced-moonbit-build.md

MoonBit Language Tour

MoonBit 语言概览

Core facts

核心特性

  • Expression‑oriented:
    if
    ,
    match
    , loops return values; the last expression is the return value.
  • References by default: Arrays/Maps/structs mutate via reference; use
    Ref[T]
    for primitive mutability.
  • Blocks: Separate top‑level items with
    ///|
    . Generate code block‑by‑block. If a blank line is desired within a block (enclosed by curly braces), add a comment line after the blank line (with or without comment text).
  • Visibility:
    fn
    is private by default;
    pub
    exposes read/construct as allowed;
    pub(all)
    allows external construction.
  • Naming convention: lower_snake for values/functions; UpperCamel for types/enums; enum variants start UpperCamel.
  • Packages: No
    import
    in code files; call via
    @alias.fn
    . Configure imports in
    moon.pkg
    .
  • Placeholders:
    ...
    is a valid placeholder in MoonBit code for incomplete implementations.
  • Global values: immutable by default and generally require type annotations.
  • Garbage collection: MoonBit has a GC, there is no lifetime annotation, there's no ownership system. Unlike Rust, like F#,
    let mut
    is only needed when you want to reassign a variable, not for mutating fields of a struct or elements of an array/map.
  • 表达式导向
    if
    match
    、循环均返回值;最后一个表达式即为返回值。
  • 默认引用语义:数组/映射/结构体通过引用可变;使用
    Ref[T]
    实现基本类型的可变性。
  • 代码块:使用
    ///|
    分隔顶级代码项。按代码块生成代码。若在代码块(花括号包裹)内需要空行,在空行后添加注释行(可带或不带注释文本)。
  • 可见性
    fn
    默认私有;
    pub
    允许外部读取/构造(受限制);
    pub(all)
    允许外部自由构造。
  • 命名规范:值/函数使用小写蛇形命名;类型/枚举使用大驼峰命名;枚举变体以大驼峰开头。
  • :代码文件中无需
    import
    ;通过
    @alias.fn
    调用其他包函数。在
    moon.pkg
    中配置导入。
  • 占位符
    ...
    是 MoonBit 中有效的未完成实现占位符。
  • 全局值:默认不可变,通常需要类型注解。
  • 垃圾回收:MoonBit 支持 GC,无需生命周期注解,无所有权系统。与 Rust 不同,类似 F#,仅当需要重新赋值变量时才需要
    let mut
    ,结构体字段或数组/映射元素的可变操作无需
    let mut

MoonBit Error Handling (Checked Errors)

MoonBit 错误处理(检查型错误)

MoonBit uses checked error-throwing functions, not unchecked exceptions. All errors are a subtype of
Error
and you can declare your own error types using
suberror
. Use
raise
in signatures to declare error types and let errors propagate by default. Use
try?
to convert to
Result[...]
in tests, or
try { } catch { }
to handle errors explicitly. Use
try!
to abort if it does raise.
mbt
///|
/// Declare error types with 'suberror'
suberror ValueError {
  ValueError(String)
}

///|
/// Tuple struct to hold position info
struct Position(Int, Int) derive(ToJson, Show, Eq)

///|
/// ParseError is subtype of Error
pub(all) suberror ParseError {
  InvalidChar(pos~ : Position, Char) // pos is labeled
  InvalidEof(pos~ : Position)
  InvalidNumber(pos~ : Position, String)
  InvalidIdentEscape(pos~ : Position)
} derive(Eq, ToJson, Show)

///|
/// Functions declare what they can throw
fn parse_int(s : String, position~ : Position) -> Int raise ParseError {
  // 'raise' throws an error
  if s is "" {
    raise ParseError::InvalidEof(pos=position)
  }
  ... // parsing logic
}

///|
/// Just declare `raise` to not track specific error types
fn div(x : Int, y : Int) -> Int raise {
  if y is 0 {
    fail("Division by zero")
  }
  x / y
}

///|
test "inspect raise function" {
  let result : Result[Int, Error] = try? div(1, 0)
  guard result is Err(Failure(msg)) && msg.contains("Division by zero") else {
    fail("Expected error")
  }
}

// Three ways to handle errors:

///|
/// Propagate automatically
fn use_parse(position~ : Position) -> Int raise ParseError {
  let x = parse_int("123", position~) // label punning, equivalent to position=position
  // Error auto-propagates by default.
  // Unlike Swift, you do not need to mark `try` for functions that can raise
  // errors; the compiler infers it automatically. This keeps error handling
  // explicit but concise.
  x * 2
}

///|
/// Mark `raise` for all possible errors, do not care which error it is.
/// For quick prototypes, `raise` is acceptable.
fn use_parse2(position~ : Position) -> Int raise {
  let x = parse_int("123", position~) // label punning
  x * 2
}

///|
/// Convert to Result with try?
fn safe_parse(s : String, position~ : Position) -> Result[Int, ParseError] {
  let val1 : Result[_] = try? parse_int(s, position~) // Returns Result[Int, ParseError]
  // try! is rarely used - it panics on error, similar to unwrap() in Rust
  // let val2 : Int = try! parse_int(s) // Returns Int otherwise crash

  // Alternative explicit handling:
  let val3 = try parse_int(s, position~) catch {
    err => Err(err)
  } noraise { // noraise block is optional - handles the success case
    v => Ok(v)
  }
  ...
}

///|
/// Handle with try-catch
fn handle_parse(s : String, position~ : Position) -> Int {
  try parse_int(s, position~) catch {
    ParseError::InvalidEof => {
      println("Parse failed: InvalidEof")
      -1 // Default value
    }
    _ => 2
  }
}
Important: When calling a function that can raise errors, if you only want to propagate the error, you do not need any marker; the compiler infers it. Note that all
async
functions automatically can raise errors without explicitly stating this.
MoonBit 使用检查型抛出错误的函数,而非未检查的异常。所有错误均为
Error
的子类型,可使用
suberror
声明自定义错误类型。在函数签名中使用
raise
声明错误类型,错误默认自动传播。在测试中使用
try?
转换为
Result[...]
,或使用
try { } catch { }
显式处理错误。使用
try!
在错误发生时终止程序。
mbt
///|
/// 使用 'suberror' 声明错误类型
suberror ValueError {
  ValueError(String)
}

///|
/// 保存位置信息的元组结构体
struct Position(Int, Int) derive(ToJson, Show, Eq)

///|
/// ParseError 是 Error 的子类型
pub(all) suberror ParseError {
  InvalidChar(pos~ : Position, Char) // pos 是带标签的字段
  InvalidEof(pos~ : Position)
  InvalidNumber(pos~ : Position, String)
  InvalidIdentEscape(pos~ : Position)
} derive(Eq, ToJson, Show)

///|
/// 函数声明可能抛出的错误类型
fn parse_int(s : String, position~ : Position) -> Int raise ParseError {
  // 'raise' 用于抛出错误
  if s is "" {
    raise ParseError::InvalidEof(pos=position)
  }
  ... // 解析逻辑
}

///|
/// 仅声明 `raise` 而不指定具体错误类型
fn div(x : Int, y : Int) -> Int raise {
  if y is 0 {
    fail("Division by zero")
  }
  x / y
}

///|
test "inspect raise function" {
  let result : Result[Int, Error] = try? div(1, 0)
  guard result is Err(Failure(msg)) && msg.contains("Division by zero") else {
    fail("Expected error")
  }
}

// 三种错误处理方式:

///|
/// 自动传播错误
fn use_parse(position~ : Position) -> Int raise ParseError {
  let x = parse_int("123", position~) // 标签省略写法,等价于 position=position
  // 错误默认自动传播。与 Swift 不同,无需为可能抛出错误的函数标记 `try`;编译器会自动推断。这使错误处理既显式又简洁。
  x * 2
}

///|
/// 声明 `raise` 表示可能抛出任意错误,不关心具体类型。对于快速原型开发,`raise` 是可接受的。
fn use_parse2(position~ : Position) -> Int raise {
  let x = parse_int("123", position~) // 标签省略写法
  x * 2
}

///|
/// 使用 try? 转换为 Result
fn safe_parse(s : String, position~ : Position) -> Result[Int, ParseError] {
  let val1 : Result[_] = try? parse_int(s, position~) // 返回 Result[Int, ParseError]
  // try! 很少使用 - 它会在错误发生时触发恐慌,类似 Rust 的 unwrap()
  // let val2 : Int = try! parse_int(s) // 返回 Int,否则崩溃

  // 另一种显式处理方式:
  let val3 = try parse_int(s, position~) catch {
    err => Err(err)
  } noraise { // noraise 块可选 - 处理成功情况
    v => Ok(v)
  }
  ...
}

///|
/// 使用 try-catch 处理错误
fn handle_parse(s : String, position~ : Position) -> Int {
  try parse_int(s, position~) catch {
    ParseError::InvalidEof => {
      println("Parse failed: InvalidEof")
      -1 // 默认值
    }
    _ => 2
  }
}
重要提示:调用可能抛出错误的函数时,若仅需传播错误,无需添加任何标记;编译器会自动推断。注意:所有
async
函数默认可抛出错误,无需显式声明。

Integers, Char

整数、字符

MoonBit supports
Byte
,
Int16
,
Int
,
UInt16
,
UInt
,
Int64
,
UInt64
, etc. When the type is known, the literal can be overloaded:
mbt
///|
test "integer and char literal overloading disambiguation via type in the current context" {
  let a0 = 1 // a is Int by default
  let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
    1, 1, 1, 1, 1,
  )
  assert_eq(int, uint16.to_int())
  let a1 : Int = 'b' // this also works, a5 will be the unicode value
  let a2 : Char = 'b'

}
MoonBit 支持
Byte
Int16
Int
UInt16
UInt
Int64
UInt64
等类型。当类型已知时,字面量会被重载:
mbt
///|
test "integer and char literal overloading disambiguation via type in the current context" {
  let a0 = 1 // 默认类型是 Int
  let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
    1, 1, 1, 1, 1,
  )
  assert_eq(int, uint16.to_int())
  let a1 : Int = 'b' // 这也有效,a5 会是对应的 Unicode 值
  let a2 : Char = 'b'

}

Bytes (Immutable)

字节(不可变)

mbt
///|
test "bytes literals overloading and indexing" {
  let b0 : Bytes = b"abcd"
  let b1 : Bytes = "abcd" // b" prefix is optional, when we know the type
  let b2 : Bytes = [0xff, 0x00, 0x01] // Array literal overloading
  guard b0 is [b'a', ..] && b0[1] is b'b' else {
    // Bytes can be pattern matched as BytesView and indexed
    fail("unexpected bytes content")
  }
}
mbt
///|
test "bytes literals overloading and indexing" {
  let b0 : Bytes = b"abcd"
  let b1 : Bytes = "abcd" // 当类型已知时,b" 前缀可选
  let b2 : Bytes = [0xff, 0x00, 0x01] // 数组字面量重载
  guard b0 is [b'a', ..] && b0[1] is b'b' else {
    // Bytes 可作为 BytesView 进行模式匹配和索引
    fail("unexpected bytes content")
  }
}

Array (Resizable)

数组(可调整大小)

mbt
///|
test "array literals overloading: disambiguation via type in the current context" {
  let a0 : Array[Int] = [1, 2, 3] // resizable
  let a1 : FixedArray[Int] = [1, 2, 3] // Fixed size
  let a2 : ReadOnlyArray[Int] = [1, 2, 3]
  let a3 : ArrayView[Int] = [1, 2, 3]

}
mbt
///|
test "array literals overloading: disambiguation via type in the current context" {
  let a0 : Array[Int] = [1, 2, 3] // 可调整大小
  let a1 : FixedArray[Int] = [1, 2, 3] // 固定大小
  let a2 : ReadOnlyArray[Int] = [1, 2, 3]
  let a3 : ArrayView[Int] = [1, 2, 3]

}

String (Immutable UTF-16)

字符串(不可变 UTF-16)

s[i]
returns a code unit (UInt16),
s.get_char(i)
returns
Char?
. Since MoonBit supports char literal overloading, you can write code snippets like this:
mbt
///|
test "string indexing and utf8 encode/decode" {
  let s = "hello world"
  let b0 : UInt16 = s[0]
  guard b0 is ('\n' | 'h' | 'b' | 'a'..='z') && s is [.. "hello", .. rest] else {
    fail("unexpected string content")
  }
  guard rest is " world"  // otherwise will crash (guard without else)

  // In check mode (expression with explicit type), ('\n' : UInt16) is valid.

  // Using get_char for Option handling
  let b1 : Char? = s.get_char(0)
  assert_true(b1 is Some('a'..='z'))

  // ⚠️ Important: Variables won't work with direct indexing
  let eq_char : Char = '='
  // s[0] == eq_char // ❌ Won't compile - eq_char is not a literal, lhs is UInt while rhs is Char
  // Use: s[0] == '=' or s.get_char(0) == Some(eq_char)
  let bytes = @utf8.encode("中文") // utf8 encode package is in stdlib
  assert_true(bytes is [0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87])
  let s2 : String = @utf8.decode(bytes) // decode utf8 bytes back to String
  assert_true(s2 is "中文")
  for c in "中文" {
    let _ : Char = c // unicode safe iteration
    println("char: \{c}") // iterate over chars
  }
}
s[i]
返回代码单元(UInt16),
s.get_char(i)
返回
Char?
。由于 MoonBit 支持字符字面量重载,可编写如下代码:
mbt
///|
test "string indexing and utf8 encode/decode" {
  let s = "hello world"
  let b0 : UInt16 = s[0]
  guard b0 is ('\n' | 'h' | 'b' | 'a'..='z') && s is [.. "hello", .. rest] else {
    fail("unexpected string content")
  }
  guard rest is " world"  // 否则会崩溃(guard 没有 else 分支)

  // 在检查模式(带显式类型的表达式)中,('\n' : UInt16) 是有效的。

  // 使用 get_char 处理 Option
  let b1 : Char? = s.get_char(0)
  assert_true(b1 is Some('a'..='z'))

  // ⚠️ 重要:变量不能直接用于索引比较
  let eq_char : Char = '='
  // s[0] == eq_char // ❌ 编译失败 - eq_char 不是字面量,左边是 UInt 右边是 Char
  // 正确写法:s[0] == '=' 或 s.get_char(0) == Some(eq_char)
  let bytes = @utf8.encode("中文") // utf8 编码包在标准库中
  assert_true(bytes is [0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87])
  let s2 : String = @utf8.decode(bytes) // 将 utf8 字节解码回 String
  assert_true(s2 is "中文")
  for c in "中文" {
    let _ : Char = c // 安全的 Unicode 迭代
    println("char: \{c}") // 遍历字符
  }
}

String Interpolation && StringBuilder

字符串插值 && 字符串构建器

MoonBit uses
\{}
for string interpolation, for custom types, they need to implement trait
Show
.
mbt
///|
test "string interpolation basics" {
  let name : String = "Moon"
  let config = { "cache": 123 }
  let version = 1.0
  println("Hello \{name} v\{version}") // "Hello Moon v1.0"
  // ❌ Wrong - quotes inside interpolation not allowed:
  // println("  - Checking if 'cache' section exists: \{config["cache"]}")

  // ✅ Correct - extract to variable first:
  let has_key = config["cache"] // `"` not allowed in interpolation
  println("  - Checking if 'cache' section exists: \{has_key}")
  let sb = StringBuilder::new()
  sb
  ..write_char('[') // dotdot for imperative method chaining
  ..write_view([1, 2, 3].map(x => "\{x}").join(","))
  ..write_char(']')
  inspect(sb.to_string(), content="[1,2,3]")
}
Expressions inside
\{}
can only be basic expressions (no quotes, newlines, or nested interpolations). String literals are not allowed as they make lexing too difficult.
MoonBit 使用
\{}
进行字符串插值,自定义类型需要实现
Show
trait。
mbt
///|
test "string interpolation basics" {
  let name : String = "Moon"
  let config = { "cache": 123 }
  let version = 1.0
  println("Hello \{name} v\{version}") // 输出 "Hello Moon v1.0"
  // ❌ 错误写法 - 插值内不允许使用引号:
  // println("  - Checking if 'cache' section exists: \{config["cache"]}")

  // ✅ 正确写法 - 先提取到变量:
  let has_key = config["cache"] // 插值内不允许使用 `"`
  println("  - Checking if 'cache' section exists: \{has_key}")
  let sb = StringBuilder::new()
  sb
  ..write_char('[') // 使用 dotdot 进行命令式方法链式调用
  ..write_view([1, 2, 3].map(x => "\{x}").join(","))
  ..write_char(']')
  inspect(sb.to_string(), content="[1,2,3]")
}
\{}
内的表达式只能是基础表达式(不能包含引号、换行或嵌套插值)。字符串字面量不允许出现在插值中,这会使词法分析过于复杂。

Multiple line strings

多行字符串

mbt
///|
test "multi-line string literals" {
  let multi_line_string : String =
    #|Hello "world"
    #|World
    #|
  let multi_line_string_with_interp : String =
    $|Line 1 ""
    $|Line 2 \{1+2}
    $|
  // no escape in `#|`,
  // only escape '\{..}` in `$|`
  assert_eq(multi_line_string, "Hello \"world\"\nWorld\n")
  assert_eq(multi_line_string_with_interp, "Line 1 \"\"\nLine 2 3\n")
}
mbt
///|
test "multi-line string literals" {
  let multi_line_string : String =
    #|Hello "world"
    #|World
    #|
  let multi_line_string_with_interp : String =
    $|Line 1 ""
    $|Line 2 \{1+2}
    $|
  // `#|` 内无需转义,
  // `$|` 内仅需转义 `\{..}`
  assert_eq(multi_line_string, "Hello \"world\"\nWorld\n")
  assert_eq(multi_line_string_with_interp, "Line 1 \"\"\nLine 2 3\n")
}

Map (Mutable, Insertion-Order Preserving)

映射(可变,保留插入顺序)

mbt
///|
test "map literals and common operations" {
  // Map literal syntax
  let map : Map[String, Int] = { "a": 1, "b": 2, "c": 3 }
  let empty : Map[String, Int] = {} // Empty map, preferred
  let also_empty : Map[String, Int] = Map::new()
  // From array of pairs
  let from_pairs : Map[String, Int] = Map::from_array([("x", 1), ("y", 2)])

  // Set/update value
  map["new-key"] = 3
  map["a"] = 10 // Updates existing key

  // Get value - returns Option[T]
  guard map is { "new-key": 3, "missing"? : None, .. } else {
    fail("unexpected map contents")
  }

  // Direct access (panics if key missing)
  let value : Int = map["a"] // value = 10

  // Iteration preserves insertion order
  for k, v in map {
    println("\{k}: \{v}") // Prints: a: 10, b: 2, c: 3, new-key: 3
  }

  // Other common operations
  map.remove("b")
  guard map is { "a": 10, "c": 3, "new-key": 3, .. } && map.length() == 3 else {
    // "b" is gone, only 3 elements left
    fail("unexpected map contents after removal")
  }
}
mbt
///|
test "map literals and common operations" {
  // 映射字面量语法
  let map : Map[String, Int] = { "a": 1, "b": 2, "c": 3 }
  let empty : Map[String, Int] = {} // 推荐的空映射写法
  let also_empty : Map[String, Int] = Map::new()
  // 从键值对数组创建映射
  let from_pairs : Map[String, Int] = Map::from_array([("x", 1), ("y", 2)])

  // 设置/更新值
  map["new-key"] = 3
  map["a"] = 10 // 更新已有键

  // 获取值 - 返回 Option[T]
  guard map is { "new-key": 3, "missing"? : None, .. } else {
    fail("unexpected map contents")
  }

  // 直接访问(键不存在时触发恐慌)
  let value : Int = map["a"] // value = 10

  // 迭代保留插入顺序
  for k, v in map {
    println("\{k}: \{v}") // 输出: a: 10, b: 2, c: 3, new-key: 3
  }

  // 其他常见操作
  map.remove("b")
  guard map is { "a": 10, "c": 3, "new-key": 3, .. } && map.length() == 3 else {
    // "b" 已被移除,仅剩 3 个元素
    fail("unexpected map contents after removal")
  }
}

View Types

视图类型

Key Concept: View types (
StringView
,
BytesView
,
ArrayView[T]
) are zero-copy, non-owning read-only slices created with the
[:]
syntax. They don't allocate memory and are ideal for passing sub-sequences without copying data, for functions which take
String
,
Bytes
,
Array
, they also take
*View
(implicit conversion).
  • String
    StringView
    via
    s[:]
    or
    s[start:end]
    or
    s[start:]
    or
    s[:end]
  • Bytes
    BytesView
    via
    b[:]
    or
    b[start:end]
    , etc.
  • Array[T]
    ,
    FixedArray[T]
    ,
    ReadOnlyArray[T] → 
    ArrayView[T]
    via
    a[:]
    or
    a[start:end]`, etc.
Important: StringView slice is slightly different due to unicode safety:
s[a:b]
may raise an error at surrogate boundaries (UTF-16 encoding edge case). You have two options:
  • Use
    try! s[a:b]
    if you're certain the boundaries are valid (crashes on invalid boundaries)
  • Let the error propagate to the caller for proper handling
When to use views:
  • Pattern matching with rest patterns (
    [first, .. rest]
    )
  • Passing slices to functions without allocation overhead
  • Avoiding unnecessary copies of large sequences
Convert back with
.to_string()
,
.to_bytes()
, or
.to_array()
when you need ownership. (
moon ide doc StringView
)
核心概念:视图类型(
StringView
BytesView
ArrayView[T]
)是零拷贝、非所有权的只读切片,通过
[:]
语法创建。它们不分配内存,非常适合传递子序列而无需复制数据,对于接受
String
Bytes
Array
的函数,也可接受对应的
*View
类型(隐式转换)。
  • String
    StringView
    :通过
    s[:]
    s[start:end]
    s[start:]
    s[:end]
  • Bytes
    BytesView
    :通过
    b[:]
    b[start:end]
  • Array[T]
    FixedArray[T]
    ReadOnlyArray[T]
    ArrayView[T]
    :通过
    a[:]
    a[start:end]
重要提示:StringView 切片由于 Unicode 安全性略有不同:
s[a:b]
在代理对边界处可能抛出错误。有两种处理方式:
  • 若确定边界有效,使用
    try! s[a:b]
    (无效边界时崩溃)
  • 让错误传播给调用方进行适当处理
何时使用视图
  • 使用剩余模式进行模式匹配(
    [first, .. rest]
  • 传递切片给函数时避免分配开销
  • 避免大序列的不必要复制
当需要所有权时,使用
.to_string()
.to_bytes()
.to_array()
转换回原类型。(可通过
moon ide doc StringView
查看详情)

User defined types(
enum
,
struct
)

用户自定义类型(
enum
struct

mbt
///|
enum Tree[T] {
  Leaf(T) // Unlike Rust, no comma here
  Node(left~ : Tree[T], T, right~ : Tree[T]) // enum can use labels
} derive(Show, ToJson) // derive traits for Tree

///|
pub fn Tree::sum(tree : Tree[Int]) -> Int {
  match tree {
    Leaf(x) => x
    // we don't need to write Tree::Leaf, when `tree` has a known type
    Node(left~, x, right~) => left.sum() + x + right.sum() // method invoked in dot notation
  }
}

///|
struct Point {
  x : Int
  y : Int
} derive(Show, ToJson) // derive traits for Point

///|
test "user defined types: enum and struct" {
  @json.inspect(Point::{ x: 10, y: 20 }, content={ "x": 10, "y": 20 })
}
mbt
///|
enum Tree[T] {
  Leaf(T) // 与 Rust 不同,此处不需要逗号
  Node(left~ : Tree[T], T, right~ : Tree[T]) // 枚举可使用带标签的字段
} derive(Show, ToJson) // 为 Tree 派生 trait

///|
pub fn Tree::sum(tree : Tree[Int]) -> Int {
  match tree {
    Leaf(x) => x
    // 由于 `tree` 类型已知,无需写 Tree::Leaf
    Node(left~, x, right~) => left.sum() + x + right.sum() // 使用点符号调用方法
  }
}

///|
struct Point {
  x : Int
  y : Int
} derive(Show, ToJson) // 为 Point 派生 trait

///|
test "user defined types: enum and struct" {
  @json.inspect(Point::{ x: 10, y: 20 }, content={ "x": 10, "y": 20 })
}

Functional
for
loop

函数式
for
循环

mbt
///|
pub fn binary_search(arr : ArrayView[Int], value : Int) -> Result[Int, Int] {
  let len = arr.length()
  // functional for loop:
  // initial state ; [predicate] ; [post-update] {
  // loop body with `continue` to update state
  //} else { // exit block
  // }
  // predicate and post-update are optional
  for i = 0, j = len; i < j; {
    // post-update is omitted, we use `continue` to update state
    let h = i + (j - i) / 2
    if arr[h] < value {
      continue h + 1, j // functional update of loop state
    } else {
      continue i, h // functional update of loop state
    }
  } else { // exit of for loop
    if i < len && arr[i] == value {
      Ok(i)
    } else {
      Err(i)
    }
  } where {
    invariant: 0 <= i && i <= j && j <= len,
    invariant: i == 0 || arr[i - 1] < value,
    invariant: j == len || arr[j] >= value,
    reasoning: (
      #|For a sorted array, the boundary invariants are witnesses:
      #|  - `arr[i-1] < value` implies all arr[0..i) < value (by sortedness)
      #|  - `arr[j] >= value` implies all arr[j..len) >= value (by sortedness)
      #|
      #|Preservation proof:
      #|  - When arr[h] < value: new_i = h+1, and arr[new_i - 1] = arr[h] < value ✓
      #|  - When arr[h] >= value: new_j = h, and arr[new_j] = arr[h] >= value ✓
      #|
      #|Termination: j - i decreases each iteration (h is strictly between i and j)
      #|
      #|Correctness at exit (i == j):
      #|  - By invariants: arr[0..i) < value and arr[i..len) >= value
      #|  - So if value exists, it can only be at index i
      #|  - If arr[i] != value, then value is absent and i is the insertion point
      #|
    ),
  }
}

///|
test "functional for loop control flow" {
  let arr : Array[Int] = [1, 3, 5, 7, 9]
  inspect(binary_search(arr, 5), content="Ok(2)") // Array to ArrayView implicit conversion when passing as arguments
  inspect(binary_search(arr, 6), content="Err(3)")
  // for iteration is supported too
  for i, v in arr {
    println("\{i}: \{v}") // `i` is index, `v` is value
  }
}
You are STRONGLY ENCOURAGED to use functional
for
loops instead of imperative loops WHENEVER POSSIBLE, as they are easier to read and reason about.
mbt
///|
pub fn binary_search(arr : ArrayView[Int], value : Int) -> Result[Int, Int] {
  let len = arr.length()
  // 函数式 for 循环:
  // 初始状态 ; [循环条件] ; [后更新操作] {
  // 循环体中使用 `continue` 更新状态
  //} else { // 退出块
  // }
  // 循环条件和后更新操作是可选的
  for i = 0, j = len; i < j; {
    // 省略后更新操作,使用 `continue` 更新状态
    let h = i + (j - i) / 2
    if arr[h] < value {
      continue h + 1, j // 函数式更新循环状态
    } else {
      continue i, h // 函数式更新循环状态
    }
  } else { // 循环退出块
    if i < len && arr[i] == value {
      Ok(i)
    } else {
      Err(i)
    }
  } where {
    invariant: 0 <= i && i <= j && j <= len,
    invariant: i == 0 || arr[i - 1] < value,
    invariant: j == len || arr[j] >= value,
    reasoning: (
      #|对于排序数组,边界不变量可作为见证:
      #|  - `arr[i-1] < value` 意味着所有 arr[0..i) < value(由数组有序性保证)
      #|  - `arr[j] >= value` 意味着所有 arr[j..len) >= value(由数组有序性保证)
      #|
      #|不变量保持证明:
      #|  - 当 arr[h] < value 时:new_i = h+1,且 arr[new_i - 1] = arr[h] < value ✓
      #|  - 当 arr[h] >= value 时:new_j = h,且 arr[new_j] = arr[h] >= value ✓
      #|
      #|终止性证明:j - i 在每次迭代中递减(h 严格介于 i 和 j 之间)
      #|
      #|退出时的正确性(i == j):
      #|  - 由不变量可知:arr[0..i) < value 且 arr[i..len) >= value
      #|  - 因此若值存在,只能在索引 i 处
      #|  - 若 arr[i] != value,则值不存在,i 是插入位置
      #|
    ),
  }
}

///|
test "functional for loop control flow" {
  let arr : Array[Int] = [1, 3, 5, 7, 9]
  inspect(binary_search(arr, 5), content="Ok(2)") // 传递参数时 Array 会隐式转换为 ArrayView
  inspect(binary_search(arr, 6), content="Err(3)")
  // 也支持 for 迭代
  for i, v in arr {
    println("\{i}: \{v}") // `i` 是索引,`v` 是值
  }
}
强烈建议:只要可能,就使用函数式
for
循环而非命令式循环,因为函数式循环更易读和推理。

Loop Invariants with
where
Clause

where
子句的循环不变量

The
where
clause attaches machine-checkable invariants and human-readable reasoning to functional
for
loops. This enables formal verification thinking while keeping the code executable. Note for trivial loops, you are encouraged to convert it into
for .. in
so no reasoning is needed.
Syntax:
mbt
for ... {
  ...
} where {
  invariant : <boolean_expr>,   // checked at runtime in debug builds
  invariant : <boolean_expr>,   // multiple invariants allowed
  reasoning : <string>         // documentation for proof sketch
}
Writing Good Invariants:
  1. Make invariants checkable: Invariants must be valid MoonBit boolean expressions using loop variables and captured values.
  2. Use boundary witnesses: For properties over ranges (e.g., "all elements in arr[0..i) satisfy P"), check only boundary elements. For sorted arrays,
    arr[i-1] < value
    implies all
    arr[0..i) < value
    .
  3. Handle edge cases with
    ||
    : Use patterns like
    i == 0 || arr[i-1] < value
    to handle boundary conditions where the check would be out of bounds.
  4. Cover three aspects in reasoning:
    • Preservation: Why each
      continue
      maintains the invariants
    • Termination: Why the loop eventually exits (e.g., a decreasing measure)
    • Correctness: Why the invariants at exit imply the desired postcondition
where
子句为函数式
for
循环附加机器可检查的不变量人类可读的推理。这使得在保持代码可执行的同时,能采用形式化验证的思维方式。对于简单循环,建议转换为
for .. in
循环,无需编写推理。
语法
mbt
for ... {
  ...
} where {
  invariant : boolean_expr,   // 调试构建时会在运行时检查
  invariant : boolean_expr,   // 允许多个不变量
  reasoning : string         // 证明草图的文档
}
编写优质不变量的技巧
  1. 确保不变量可检查:不变量必须是有效的 MoonBit 布尔表达式,使用循环变量和捕获的值。
  2. 使用边界见证:对于范围属性(例如 "arr[0..i) 中的所有元素满足 P"),仅检查边界元素。对于排序数组,
    arr[i-1] < value
    意味着所有
    arr[0..i) < value
  3. 使用
    ||
    处理边界情况
    :使用
    i == 0 || arr[i-1] < value
    这样的模式处理检查可能越界的边界条件。
  4. 推理部分涵盖三个方面
    • 保持性:为什么每个
      continue
      操作能保持不变量
    • 终止性:为什么循环最终会退出(例如某个递减的度量值)
    • 正确性:为什么退出时的不变量能推导出期望的后置条件

Label and Optional Parameters

标签与可选参数

Good example: use labeled and optional parameters
mbt
///|
fn g(
  positional : Int,
  required~ : Int,
  optional? : Int, // no default => Option
  optional_with_default? : Int = 42, // default => plain Int
) -> String {
  // These are the inferred types inside the function body.
  let _ : Int = positional
  let _ : Int = required
  let _ : Int? = optional
  let _ : Int = optional_with_default
  "\{positional},\{required},\{optional},\{optional_with_default}"
}

///|
test {
  inspect(g(1, required=2), content="1,2,None,42")
  inspect(g(1, required=2, optional=3), content="1,2,Some(3),42")
  inspect(g(1, required=4, optional_with_default=100), content="1,4,None,100")
}
Misuse:
arg : Type?
is not an optional parameter. Callers still must pass it (as
None
/
Some(...)
).
mbt
///|
fn with_config(a : Int?, b : Int?, c : Int) -> String {
  "\{a},\{b},\{c}"
}

///|
test {
  inspect(with_config(None, None, 1), content="None,None,1")
  inspect(with_config(Some(5), Some(5), 1), content="Some(5),Some(5),1")
}
Anti-pattern:
arg? : Type?
(no default => double Option). If you want a defaulted optional parameter, write
b? : Int = 1
, not
b? : Int? = Some(1)
.
mbt
///|
fn f_misuse(a? : Int?, b? : Int = 1) -> Unit {
  let _ : Int?? = a // rarely intended
  let _ : Int = b

}
// How to fix: declare `(a? : Int, b? : Int = 1)` directly.

///|
fn f_correct(a? : Int, b? : Int = 1) -> Unit {
  let _ : Int? = a
  let _ : Int = b

}

///|
test {
  f_misuse(b=3)
  f_misuse(a=Some(5), b=2) // works but confusing
  f_correct(b=2)
  f_correct(a=5)
}
Bad example:
arg : APIOptions
(use labeled optional parameters instead)
mbt
///|
/// Do not use struct to group options.
struct APIOptions {
  width : Int?
  height : Int?
}

///|
fn not_idiomatic(opts : APIOptions, arg : Int) -> Unit {

}

///|
test {
  // Hard to use in call site
  not_idiomatic({ width: Some(5), height: None }, 10)
  not_idiomatic({ width: None, height: None }, 10)
}
良好示例:使用标签和可选参数
mbt
///|
fn g(
  positional : Int,
  required~ : Int,
  optional? : Int, // 无默认值 => Option 类型
  optional_with_default? : Int = 42, // 有默认值 => 普通 Int 类型
) -> String {
  // 函数体内的推断类型如下
  let _ : Int = positional
  let _ : Int = required
  let _ : Int? = optional
  let _ : Int = optional_with_default
  "\{positional},\{required},\{optional},\{optional_with_default}"
}

///|
test {
  inspect(g(1, required=2), content="1,2,None,42")
  inspect(g(1, required=2, optional=3), content="1,2,Some(3),42")
  inspect(g(1, required=4, optional_with_default=100), content="1,4,None,100")
}
错误用法:
arg : Type?
不是可选参数。调用方仍必须传递该参数(作为
None
/
Some(...)
)。
mbt
///|
fn with_config(a : Int?, b : Int?, c : Int) -> String {
  "\{a},\{b},\{c}"
}

///|
test {
  inspect(with_config(None, None, 1), content="None,None,1")
  inspect(with_config(Some(5), Some(5), 1), content="Some(5),Some(5),1")
}
反模式:
arg? : Type?
(无默认值 => 双重 Option)。若需要带默认值的可选参数,应写
b? : Int = 1
,而非
b? : Int? = Some(1)
mbt
///|
fn f_misuse(a? : Int?, b? : Int = 1) -> Unit {
  let _ : Int?? = a // 很少是预期的
  let _ : Int = b

}
// 修复方式:直接声明 `(a? : Int, b? : Int = 1)`。

///|
fn f_correct(a? : Int, b? : Int = 1) -> Unit {
  let _ : Int? = a
  let _ : Int = b

}

///|
test {
  f_misuse(b=3)
  f_misuse(a=Some(5), b=2) // 可行但易混淆
  f_correct(b=2)
  f_correct(a=5)
}
不良示例:
arg : APIOptions
(应使用标签可选参数替代)
mbt
///|
/// 不要使用结构体来分组选项。
struct APIOptions {
  width : Int?
  height : Int?
}

///|
fn not_idiomatic(opts : APIOptions, arg : Int) -> Unit {

}

///|
test {
  // 调用时难以使用
  not_idiomatic({ width: Some(5), height: None }, 10)
  not_idiomatic({ width: None, height: None }, 10)
}

More details

更多细节

For deeper syntax, types, and examples, read
references/moonbit-language-fundamentals.mbt.md
.
如需更深入的语法、类型和示例,请阅读
references/moonbit-language-fundamentals.mbt.md