moonbit-agent-guide
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAgent Workflow
Agent工作流
For fast, reliable task execution, follow this order:
-
Clarify goal and constraints
- Confirm expected behavior, non-goals, and compatibility constraints (target backend, public API stability, performance limits).
-
Locate module/package boundaries
- Find (module root) and relevant
moon.mod.jsonfiles (package boundaries and imports).moon.pkg
- Find
-
Discover APIs before coding
- Prefer queries to discover existing functions/types/methods before adding new code.
moon ide doc - Use ,
moon ide outline, andmoon ide peek-deffor semantic navigation.moon ide find-references
- Prefer
-
Reliable refactoring
- Use for semantic refactoring. If multiple symbols share a name, add
moon ide rename.--loc filename:line:col - If you want maintain backwards compatibility, use .
#alias(old_api, deprecated)
- Use
-
Edit minimally and package-locally
- Keep changes inside the correct package, use top-level delimiters, and split code into cohesive files.
///|
- Keep changes inside the correct package, use
-
Validate in a tight loop
- Run after edits, adding
moon checkto enable warning 73 for redundant annotations and over-qualified constructors (--warn-list +unnecessary_annotationis equivalent).--warn-list +73 - Run targeted tests with and use
moon test [dirname|filename] --filter 'glob'for snapshot changes.moon test --update
- Run
-
Finalize before handoff
- Run .
moon fmt - Run to verify whether public APIs changed (
moon infodiff).pkg.generated.mbti - Report changed files, validation commands, and any remaining risks.
- Run
为了快速、可靠地执行任务,请遵循以下步骤:
-
明确目标与约束
- 确认预期行为、非目标需求以及兼容性约束(目标后端、公共API稳定性、性能限制)。
-
定位模块/包边界
- 找到(模块根目录标识)和相关
moon.mod.json文件(包边界与导入配置)。moon.pkg
- 找到
-
编码前先探索API
- 在添加新代码前,优先使用查询来发现现有函数/类型/方法。
moon ide doc - 使用、
moon ide outline和moon ide peek-def进行语义导航。moon ide find-references
- 在添加新代码前,优先使用
-
可靠重构
- 使用进行语义重构。若多个符号重名,添加
moon ide rename参数指定位置。--loc filename:line:col - 如需保持向后兼容性,使用。
#alias(old_api, deprecated)
- 使用
-
最小化编辑并保持包内本地化
- 将修改限制在正确的包内,使用顶级分隔符,将代码拆分为内聚性强的文件。
///|
- 将修改限制在正确的包内,使用
-
循环验证
- 编辑后运行,添加
moon check启用警告73(冗余注解和过度限定构造函数,--warn-list +unnecessary_annotation等效)。--warn-list +73 - 使用运行针对性测试,使用
moon test [dirname|filename] --filter 'glob'处理快照变更。moon test --update
- 编辑后运行
-
交付前最终确认
- 运行格式化代码。
moon fmt - 运行验证公共API是否变更(对比
moon info差异)。pkg.generated.mbti - 报告变更文件、验证命令以及剩余风险。
- 运行
Fast Task Playbooks
快速任务执行手册
Use the smallest playbook that matches the request.
选择与需求匹配的最小执行流程。
Bug Fix (No API Change Intended)
Bug修复(无API变更需求)
- Reproduce or identify the failing behavior.
- Locate symbols with ,
moon ide outline,moon ide peek-def.moon ide find-references - Implement minimal fix in the current package.
- Validate with:
moon check- (or closest targeted test scope)
moon test [dirname|filename] --filter 'glob' moon fmt- (confirm
moon infounchanged)pkg.generated.mbti
- 复现或定位故障行为。
- 使用、
moon ide outline、moon ide peek-def定位符号。moon ide find-references - 在当前包内实现最小修复。
- 验证步骤:
moon check- (或最接近的针对性测试范围)
moon test [dirname|filename] --filter 'glob' moon fmt- (确认
moon info无变更)pkg.generated.mbti
Refactor (Behavior Preserving)
重构(保留行为)
- Confirm behavior/API invariants first.
- Prefer semantic rename/navigation tools:
moon ide renamemoon ide find-referencesmoon ide peek-def- If multiple symbols share a name, use .
moon ide rename <symbol> <new_name> --loc filename:line:col
- Keep edits package-local and file-organization-focused.
- Validate with:
moon checkmoon test [dirname|filename]moon fmt- (API should remain unchanged unless requested)
moon info
- 先确认行为/API不变量。
- 优先使用语义重命名/导航工具:
moon ide renamemoon ide find-referencesmoon ide peek-def- 若多个符号重名,使用。
moon ide rename <symbol> <new_name> --loc filename:line:col
- 保持编辑在包内,聚焦文件组织优化。
- 验证步骤:
moon checkmoon test [dirname|filename]moon fmt- (除非有需求,否则API应保持不变)
moon info
New Feature or Public API
新功能或公共API开发
- Discover existing idioms with before introducing new names.
moon ide doc - Add implementation in cohesive files with delimiters.
///| - Add/extend black-box tests and docstring examples for public APIs.
- Validate with:
moon check- (use
moon test [dirname|filename]for snapshots when needed)--update moon fmt- (review and keep intended
moon infochanges)pkg.generated.mbti
- 在引入新名称前,使用探索现有编程范式。
moon ide doc - 使用分隔符将实现代码放在内聚性文件中。
///| - 为公共API添加/扩展黑盒测试和文档字符串示例。
- 验证步骤:
moon check- (必要时使用
moon test [dirname|filename]更新快照)--update moon fmt- (查看并保留预期的
moon info变更)pkg.generated.mbti
MoonBit Project Layouts
MoonBit项目布局
MoonBit uses the extension for source code files and interface files with the extension. At
the top-level of a MoonBit project there is a file specifying
the metadata of the project. The project may contain multiple packages, each
with its own . Subdirectories may also contain
files indicating that a different set of dependencies can be used for that subdir.
.mbt.mbtimoon.mod.jsonmoon.pkgmoon.mod.jsonMoonBit使用作为源代码文件扩展名,作为接口文件扩展名。MoonBit项目的顶级目录下有一个文件,用于指定项目元数据。项目可包含多个包,每个包都有自己的文件。子目录也可能包含文件,表示该子目录可使用不同的依赖集。
.mbt.mbtimoon.mod.jsonmoon.pkgmoon.mod.jsonExample 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 afile 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.
moon.mod.json -
Package: characterized by afile in each directory. All subcommands of
moon.pkgwill still be executed in the directory of the module (wheremoonis 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. Themoon.mod.jsonin thenamefile 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.moon.mod.json -
Files: Afile 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.
.mbt
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)-
模块:由项目根目录下的文件标识。 MoonBit模块类似Go模块,是子目录中包的集合,通常对应一个仓库或项目。 模块边界对依赖管理和导入路径至关重要。
moon.mod.json -
包:由每个目录下的文件标识。
moon.pkg的所有子命令仍在模块目录(moon所在目录)中执行,而非当前包目录。 MoonBit包是实际的编译单元(类似Go包)。 同一包内的所有源文件会被合并为一个单元,因此包内所有定义都可互相引用。 包名称由moon.mod.json中的moon.mod.json字段加上包源目录的相对路径定义,而非文件名。 导入引用的是模块+包路径,绝不是文件名。name -
文件:文件只是包内的一段源代码。 文件名不会创建模块、包或命名空间。 你可以自由地在同一包内的文件之间拆分/合并/移动声明。 包内的任何声明都可以引用同一包内的其他声明,无论所在文件是什么。
.mbt
Coding/layout rules you MUST follow:
必须遵循的编码/布局规则:
-
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.
-
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.
- Each block is separated by
-
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.
-
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.
-
Tests:
- Place tests in dedicated test files (e.g. ) within the appropriate package. For a package (besides
*_test.mbtfiles),*_test.mbtfiles are also blackbox test files in addition to Markdown files. The code blocks (separated by triple backticks)*.mbt.mdare treated as test cases and serve both purposes: documentation and tests. You may havembt checkfiles withREADME.mbt.mdcode examples. You can also symlinkmbt checktoREADME.mbt.mdto make it integrate better with GitHub.README.md - It is fine — and encouraged — to have multiple small test files.
- Place tests in dedicated test files (e.g.
-
Interface files ()
pkg.generated.mbtifiles 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 usingpkg.generated.mbtiand useful for code review. When you have a commit that does not change public APIs,moon infofiles will remain unchanged, so it is recommended to putpkg.generated.mbtiin version control when you are done.pkg.generated.mbtiFor IDE navigation and symbol lookup commands, see the dedicatedsection below.moon ide
-
优先使用多个小而内聚的文件,而非单个大文件。
- 将相关类型和函数分组到聚焦的文件中(例如http_client.mbt、router.mbt)。
- 如果文件变得过大或不够聚焦,创建新文件并将相关声明移至其中。
-
你可以自由地在同一包内的文件之间移动声明。
- 每个代码块由分隔。只要函数/结构体/trait的名称和可见性保持不变,在文件之间移动它们不会改变语义。代码块的顺序也无关紧要。
///| - 通过拆分或合并包内文件进行重构是安全的。
- 每个代码块由
-
文件名仅用于组织。
- 不要假设文件名定义了模块,也不要在类型路径中使用文件名。
- 选择文件名来描述功能或职责,而非严格镜像类型名称。
-
添加新代码时:
- 优先将其添加到匹配该功能的现有文件中。
- 如果没有合适的文件,在同一包下创建一个具有描述性名称的新文件。
- 避免创建庞大的“impl”、“misc”或“util”文件。
-
测试:
- 将测试放在相应包内的专用测试文件中(例如)。 对于包(除
*_test.mbt文件外),*_test.mbt文件既是Markdown文档,也是黑盒测试文件。 由反引号分隔的代码块*.mbt.md会被视为测试用例,兼具文档和测试功能。 你可以在mbt check文件中包含README.mbt.md代码示例。也可以将mbt check软链接到README.mbt.md,以更好地与GitHub集成。README.md - 拥有多个小测试文件是没问题的,甚至是推荐的。
- 将测试放在相应包内的专用测试文件中(例如
-
接口文件()
pkg.generated.mbti文件是编译器生成的每个包的公共API摘要。 它们提供了所有导出类型、函数和trait的正式、简洁概述,不含实现细节。 可通过pkg.generated.mbti生成,用于代码评审。当提交不改变公共API时,moon info文件将保持不变,因此完成后建议将其纳入版本控制。pkg.generated.mbti有关IDE导航和符号查找命令,请参阅下面专门的部分。moon ide
Common Pitfalls to Avoid
需避免的常见陷阱
- Don't use uppercase for variables/functions - compilation error
- Don't forget for mutable record fields - immutable by default (note that Arrays typically do NOT need
mutunless completely reassigning to the variable - simple push operations, for example, do not needmut)mut - Don't ignore error handling - errors must be explicitly handled
- Don't use unnecessarily - the last expression is the return value
return - Don't create methods without Type:: prefix - methods need explicit type prefix
- Don't forget to handle array bounds - use for safe access
get() - Don't forget @package prefix when calling functions from other packages
- Don't use ++ or -- (not supported) - use or
i = i + 1i += 1 - Don't add explicit for error-raising functions - errors propagate automatically (unlike Swift)
try - Legacy syntax: Legacy code may use or
function_name!(...)- these are deprecated, use normal calls.function_name(...)? - Prefer range loops over C-style -
forandfor i in 0..<(n-1) {...}are more idiomatic in MoonBitfor j in 0..=6 {...} - Async - MoonBit has no 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
awaitprefix (e.g.async,[pub] async fn ...).async test ...
- 不要为变量/函数使用大写开头 - 会导致编译错误
- 不要忘记为可变记录字段添加- 默认不可变(注意:Array通常不需要
mut,除非完全重新赋值给变量——例如简单的push操作不需要mut)mut - 不要忽略错误处理 - 错误必须显式处理
- 不要不必要地使用- 最后一个表达式即为返回值
return - 不要创建不带Type::前缀的方法 - 方法需要显式类型前缀
- 不要忘记处理数组边界 - 使用进行安全访问
get() - 调用其他包的函数时不要忘记@package前缀
- 不要使用++或--(不支持) - 使用或
i = i + 1i += 1 - 不要为抛出错误的函数添加显式- 错误会自动传播(与Swift不同)
try - 遗留语法:遗留代码可能使用或
function_name!(...)- 这些已被弃用,请使用普通调用。function_name(...)? - 优先使用范围循环而非C风格循环 -
for和for i in 0..<(n-1) {...}在MoonBit中更符合惯用写法for j in 0..=6 {...} - 异步 - MoonBit没有关键字;不要添加它。异步函数和测试的特征是调用其他异步函数。 要将函数或测试标识为异步,只需添加
await前缀(例如async、[pub] async fn ...)。async test ...
moon
Essentials
moonmoon
核心功能
moonEssential Commands
核心命令
- - Create new project
moon new my_project - - Run main package
moon run cmd/main - - Run code from stdin (useful for quick experiments)
moon run - < hello.mbt - - Run code from command line argument (good for one-liners) Example:
moon run -e "code snippet"This allows you to quickly test small snippets of MoonBit code without creating a full project. It can also be used with heredoc syntax for multi-line snippets:bashcat hello.mbt | moon run -bashmoon run - <<'EOF' fn main { println("Hello, MoonBit!") } EOFmoon run -e 'fn main { println("Hello, MoonBit!") }' - - Build project (
moon buildandmoon runboth supportmoon build)--target - - Type check without building, use it REGULARLY, it is fast (
moon checkalso supportsmoon check)--target - - Type check and generate
moon infofiles. Run it to see if any public interfaces changed. (mbtialso supportsmoon info.)--target - - Type check for all backends moon check --output-json can be used with
moon check --target allto filter the output, e.g,jqor, for richer post-processing, pipe into a small MoonBit program viamoon check --output-json 2>&1 | jq -R 'fromjson? | select(.message | contains("unused"))'. Usemoon run -e(the default--target nativedoes not supportwasm-gcorasync fn main), a quoted heredoc (@stdio.stdin) so the shell does not expand<<'EOF'/backticks in the source, and a de-indented closing$:EOFundefined
moon check --output-json 2>&1 | moon run --target native -e "$(cat <<'EOF'
import {
"moonbitlang/async",
"moonbitlang/async/stdio",
"moonbitlang/core/json",
}
async fn main {
let seen = {}
while @stdio.stdin.read_until("\n") is Some(line) {
try @json.parse(line.trim()) catch {
_ => ()
} noraise {
{"level": "warning", "path": String(p), ..} =>
if !seen.contains(p) {
seen[p] = ()
println(p)
}
_ => ()
}
}
}
EOF
)"
Get the diagnostics with "unused" in the message, which can be used to find unused code.
- `moon explain` - Show built-in documentation for compiler diagnostics.
- `moon explain --diagnostics` lists warning mnemonics and IDs.
- `moon explain --diagnostics 31` explains warning 31 (`unused_optional_argument`).
- `moon explain --diagnostics unused_optional_argument` explains the same warning by mnemonic.
- `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 run - < hello.mbt - - 从命令行参数运行代码(适合单行代码) 示例:
moon run -e "code snippet"这允许你快速测试MoonBit代码片段,无需创建完整项目。 也可结合heredoc语法用于多行片段:bashcat hello.mbt | moon run -bashmoon run - <<'EOF' fn main { println("Hello, MoonBit!") } EOFmoon run -e 'fn main { println("Hello, MoonBit!") }' - - 构建项目 (
moon build和moon run都支持moon build参数)--target - - 仅进行类型检查不构建,建议定期使用,速度很快 (
moon check也支持moon check参数)--target - - 进行类型检查并生成
moon info文件。 运行它查看公共接口是否变更。 (mbti也支持moon info参数。)--target - - 为所有后端进行类型检查 moon check --output-json可与
moon check --target all配合过滤输出,例如:jq或者,为了更丰富的后处理,通过moon check --output-json 2>&1 | jq -R 'fromjson? | select(.message | contains("unused"))'将输出传入一个小型MoonBit程序:使用moon run -e(默认的--target native不支持wasm-gc或async fn main),使用带引号的heredoc(@stdio.stdin)避免shell展开源代码中的<<'EOF'/反引号,并使用无缩进的闭合$:EOFundefined
moon check --output-json 2>&1 | moon run --target native -e "$(cat <<'EOF'
import {
"moonbitlang/async",
"moonbitlang/async/stdio",
"moonbitlang/core/json",
}
async fn main {
let seen = {}
while @stdio.stdin.read_until("\n") is Some(line) {
try @json.parse(line.trim()) catch {
_ => ()
} noraise {
{"level": "warning", "path": String(p), ..} =>
if !seen.contains(p) {
seen[p] = ()
println(p)
}
_ => ()
}
}
}
EOF
)"
获取包含"unused"的诊断信息,可用于查找未使用的代码。
- `moon explain` - 显示编译器诊断的内置文档。
- `moon explain --diagnostics`列出警告助记符和ID。
- `moon explain --diagnostics 31`解释警告31(`unused_optional_argument`)。
- `moon explain --diagnostics unused_optional_argument`通过助记符解释同一警告。
- `moon add package` - 添加依赖
- `moon remove package` - 移除依赖
- `moon fmt` - 格式化代码 - 应定期运行 - 注意文件可能会被重写
注意你也可以使用`moon -C dir check`在特定目录中运行命令。Test Commands
测试命令
- - Run all tests (
moon testalso supportsmoon test)--target - - Update snapshots
moon test --update - - Verbose output with test names
moon test -v - - Test specific directory or file
moon test [dirname|filename] - - Analyze coverage
moon coverage analyze - - Run tests matching filter
moon test [dirname|filename] --filter 'glob'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.mdREADME.mbt.md
生成指南
README.mbt.md- Output in the package directory.
README.mbt.mdfile and docstring contents treats*.mbt.mdspecially.mbt checkblock will be included directly as code and also run bymbt checkandmoon check. If you don't want the code snippets to be checked, explicitmoon testis preferred. If you are only referencing types from the package, you should usembt nocheckwhich will only be syntax highlighted. Symlinkmbt nochecktoREADME.mbt.mdto adapt to systems that expectREADME.md.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:. If unknown, write
inspect(value, content="...")and runinspect(value)(ormoon test --update).moon test -u- Use regular for simple values (uses
inspect()trait)Show - Use for complex nested structures (uses
@json.inspect()trait, produces more readable output)ToJson - It is encouraged to or
inspectthe whole return value of a function if the whole return value is not huge, this makes the test simple. You need@json.inspectorimpl (Show|ToJson) for YourType.derive (Show, ToJson)
- Use regular
-
Update workflow: After changing code that affects output, runto regenerate snapshots, then review the diffs in your test files (the
moon test --updateparameter will be updated automatically).content= -
Validation order: Follow the canonical sequence inand
Agent Workflow.Fast Task Playbooks -
Black-box by default: Call only public APIs via. Use white-box tests only when private members matter.
@package.fn -
Grouping: Combine related checks in oneblock for speed and clarity.
test "..." { ... } -
Panics: Name tests with prefix; if the call returns a value, wrap it with
test "panic ..." {...}to silence warnings.ignore(...) -
Errors: Useto get
try? f()andResult[...]it when a function may raise.inspect
使用快照测试,因为当行为变更时易于更新。
-
快照测试:。如果未知内容,编写
inspect(value, content="...")并运行inspect(value)(或moon test --update)。moon test -u- 简单值使用常规(使用
inspect()trait)Show - 复杂嵌套结构使用(使用
@json.inspect()trait,生成更易读的输出)ToJson - 如果函数的整个返回值不是很大,建议或
inspect整个返回值,这样测试更简单。你需要为自定义类型实现@json.inspect或派生(Show|ToJson)。derive (Show, ToJson)
- 简单值使用常规
-
更新流程: 修改影响输出的代码后,运行重新生成快照,然后检查测试文件中的差异(
moon test --update参数会自动更新)。content= -
验证顺序: 遵循和
Agent工作流中的标准流程。快速任务执行手册 -
默认使用黑盒测试:仅通过调用公共API。仅当私有成员很重要时才使用白盒测试。
@package.fn -
分组:将相关检查合并到一个块中,以提高速度和清晰度。
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 ). In docstrings, should only contain or .
moon test --updatembt checktestasync 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 --updatembt checktestasync testSpec-driven Development
规范驱动开发
- The spec can be written in a readonly file (name is conventional, not mandatory) with stub code marked as declarations:
spec.mbt
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, etc. to test the spec functions; everything will be type-checked(spec_difficult_test.mbt).moon check -
The AI or users can implement thefunctions in different files thanks to our package organization.
declare -
Runto check everything is correct.
moon test -
is supported for functions, methods, and types.
declare -
Theline is an intentionally opaque placeholder; the implementer chooses its representation.
pub type Yaml -
Note the spec file can also contain normal code, not just declarations.
- 规范可写在只读的文件中(名称是约定俗成的,非强制),其中存根代码标记为声明:
spec.mbt
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|analyze]
for code navigation and refactoring
moon ide [doc|peek-def|outline|find-references|hover|rename|analyze]moon ide [doc|peek-def|outline|find-references|hover|rename|analyze]
用于代码导航和重构
moon ide [doc|peek-def|outline|find-references|hover|rename|analyze]For project-local symbols and navigation, use:
- to discover available APIs, functions, types, and methods in MoonBit. Always prefer
moon ide doc <query>over other approaches when exploring what APIs are available, it is more powerful and accurate thanmoon ide docor any regex-based searching tools.grep_search - to scan a package,
moon ide outline . - to locate usages, and
moon ide find-references <symbol> - for inline definition context and to locate toplevel symbols.
moon ide peek-def - to get type information at a specific location.
moon ide hover sym --loc filename:line:col - to rename a symbol project-wide. Prefer
moon ide rename <symbol> <new_name> [--loc filename:line:col]when symbol names are ambiguous.--loc - to inspect public API usage of a package or module when planning safe refactors. These tools save tokens and are more precise than grepping (
moon ide analyze [path]displays results in both definitions and call sites including comments too).grep
对于项目本地符号和导航,使用:
- 发现MoonBit中可用的API、函数、类型和方法。探索可用API时,始终优先使用
moon ide doc <query>,它比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 - 在规划安全重构时检查包或模块的公共API使用情况。 这些工具比grep更节省时间且更精确(
moon ide analyze [path]会显示定义和调用站点的结果,包括注释)。grep
moon ide doc
for API Discovery
moon ide docmoon ide doc
用于API探索
moon ide docmoon ide doc-
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:(builtin type does not need package prefix)
moon ide doc "[@pkg.]Type_name" -
Method/field lookup:
moon ide doc "[@pkg.]Type_name::method_or_field_name" -
Package exploration:
moon ide doc "@pkg"- Show package and list all its exported symbols
pkg - Example: - explore entire
moon ide doc "@json"package@json - Example: - explore nested package
moon ide doc "@encoding/utf8"
- Show package
-
Multiple queries:
moon ide doc "query1" "query2" ...- Run multiple queries in one invocation and combine results
- Example: to explore multiple types and a package at once
moon ide doc "String" "Array" "@json"
-
Globbing: Usewildcard for partial matches, e.g.
*to find all String methods with "rev" in their namemoon ide doc "String::*rev*"
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 "query1" "query2" ...- 一次运行多个查询并合并结果
- 示例:同时探索多个类型和一个包
moon ide doc "String" "Array" "@json"
-
通配符: 使用通配符进行部分匹配,例如
*查找名称中包含"rev"的所有String方法moon ide doc "String::*rev*"
moon ide doc
Examples
moon ide docmoon ide doc
示例
moon ide docbash
undefinedbash
undefinedsearch 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
创建 ... 省略 ...
$ moon ide doc "String::rev" # 通配符
package "moonbitlang/core/string"
pub fn String::rev(String) -> String
返回 ... 省略 ...
... 更多
pub fn String::rev_find(String, StringView) -> Int?
返回 ... 省略 ...
**最佳实践**: 将此部分作为命令参考;执行顺序在`Agent工作流`中定义。moon ide rename sym new_name [--loc filename:line:col]
example
moon ide rename sym new_name [--loc filename:line:col]moon ide rename sym new_name [--loc filename:line:col]
示例
moon ide rename sym new_name [--loc filename:line:col]When the user asks: "Can you rename the function to ?"
compute_sumcalculate_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_sumcalculate_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 Patchmoon ide hover sym --loc filename:line:col
example
moon ide hover sym --loc filename:line:colmoon ide hover sym --loc filename:line:col
示例
moon ide hover sym --loc filename:line:colWhen the user asks: "What is the signature and docstring of ? at line 14 of hover.mbt"
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?
```
---
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]moon ide peek-def sym [--loc filename:line:col]
示例
moon ide peek-def sym [--loc filename:line:col]When the user asks: "Can you check if is implemented correctly?"
you can run to get the definition context
(this is better than since it searches the whole project by semantics):
Parser::read_u32_leb128moon ide peek-def Parser::read_u32_leb128grepfile
L45:|///|
L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
L47:| ...
...:| }Now if you want to see the definition of the struct, you can run:
Parserbash
$ 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 argument, the line number must be precise; the column can be approximate since
the positional argument helps locate the position.
--locParserIf 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_leb128moon ide peek-def Parser::read_u32_leb128grepfile
L45:|///|
L46:|fn Parser::read_u32_leb128(self : Parser) -> UInt raise ParseError {
L47:| ...
...:| }现在如果你想查看结构体的定义,可以运行:
Parserbash
$ 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
| }
|对于参数,行号必须精确;列号可以近似,因为位置参数有助于定位。
--locParser如果"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 | ///|
| /// 返回字符顺序反转的新字符串。它支持Unicode字符和代理对,但不支持 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>moon ide outline [dir|file]
和moon ide find-references <sym>
用于包符号
moon ide outline [dir|file]moon ide find-references <sym>Use to scan a package or file for top-level symbols and locate usages without grepping.
moon ide outline- outlines the current package directory (per-file headers)
moon ide outline dir - outlines a single file This is useful when you need a quick inventory of a package, or to find the right file before
moon ide outline parser.mbt.peek-def - finds all references to a symbol in the current module
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使用扫描包或文件的顶级符号,无需grep即可定位用法。
moon ide outline- 列出当前包目录的大纲(按文件头部)
moon ide outline dir - 列出单个文件的大纲 当你需要快速了解包的内容,或在
moon ide outline parser.mbt前找到正确文件时,这很有用。peek-def - 查找当前模块中符号的所有引用
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 TranslationUnitPackage Management
包管理
Adding Dependencies
添加依赖
sh
moon add moonbitlang/x # Add latest version
moon add moonbitlang/x@0.4.6 # Add specific versionsh
moon add moonbitlang/x # 添加最新版本
moon add moonbitlang/x@0.4.6 # 添加特定版本Updating Dependencies
更新依赖
sh
moon update # Update package indexsh
moon update # 更新包索引Browsing Third-Party Source (moon fetch
)
moon fetch浏览第三方源代码(moon fetch
)
moon fetchmoon fetch <author>/<module>[@<version>].repos/<author>/<module>/<version>/.mbtimoon.mod.jsonmoon add.repos/.gitignoresh
moon fetch moonbitlang/async@0.18.1 # browse source/examples without taking a dependencymoon fetch <author>/<module>[@<version>].repos/<author>/<module>/<version>/.mbtimoon.mod.jsonmoon add.repos/.gitignoresh
moon fetch moonbitlang/async@0.18.1 # 不添加依赖即可浏览源代码/示例Typical Module configurations (moon.mod.json
)
moon.mod.json典型模块配置(moon.mod.json
)
moon.mod.jsonjson
{
"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
)
moon.pkgmoon.pkg for simplicity
import {
"username/hello/liba",
"moonbitlang/x/encoding" @libb
}
import {...} for "test"
import {...} for "wbtest"
options("is-main" : true) // other optionsPackages are per directory and packages without a file are not recognized.
moon.pkg简化版moon.pkg
import {
"username/hello/liba",
"moonbitlang/x/encoding" @libb
}
import {...} for "test"
import {...} for "wbtest"
options("is-main" : true) // 其他选项每个目录对应一个包,没有文件的包不会被识别。
moon.pkgPackage Importing (used in moon.pkg)
包导入(用于moon.pkg)
- Import format:
"module_name/package_path" - Usage: to call imported functions
@alias.function() - Default alias: Last part of path (e.g., for
liba)username/hello/liba - Package reference: Use in test files to reference the tested package
@packagename
Package Alias Rules:
- Import → use
"username/hello/liba"(default alias is the last path segment)@liba.function() - Import with custom alias → use
import { "moonbitlang/x/encoding" @enc}(Note that this is unnecessary when the last path segment is identical to the alias name.)@enc.function() - In or
_test.mbtfiles, the package being tested is auto-imported_wbtest.mbt
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 to if you use them.
The module is always available without adding to dependencies.
moonbitlang/core/strconvmoon.pkgMoonBit标准库(moonbitlang/core)包会被自动导入。MoonBit正过渡到显式导入——如果你使用了等包,会收到警告提示你将其添加到中。
该模块始终可用,无需添加到依赖中。
moonbitlang/core/strconvmoon.pkgCreating Packages
创建包
To add a new package under :
fib.-
Create directory:
./fib/ -
Add
./fib/moon.pkg -
Addfiles with your code
.mbt -
Import in dependent packages:
import { "username/hello/fib", }
For more advanced topics like , , , and , see .
conditional compilationlink configurationwarning controlpre-build commandsreferences/advanced-moonbit-build.md要在下添加新包:
.fib-
创建目录:
./fib/ -
添加
./fib/moon.pkg -
添加包含代码的文件
.mbt -
在依赖包中导入:
import { "username/hello/fib", }
有关、、和等更高级主题,请参阅。
条件编译链接配置警告控制预构建命令references/advanced-moonbit-build.mdAsync IO
异步IO
Asynchronous programming uses compiler support plus the runtime. The runtime supports the native backend best, has limited JavaScript support for IO-independent APIs, and does not support WebAssembly yet. For async IO examples, prefer native. Use and to explore the API.
moonbitlang/asyncmoon add moonbitlang/async@<version>moon ide doc "@async"User-facing subpackages: (core: tasks, timers, cancellation), , @async/stdio@async/websocketmoon.pkg`.
@async@async/aqueue@async/fs, , , ..etc. Each must be imported separately in - Add the dependency and pin the native target in :
moon.mod.jsonjson{ "deps": { "moonbitlang/async": "0.18.1" }, "preferred-target": "native" } - In the executable's , set
moon.pkg, restrict to native, and import what you need:is-mainimport { "moonbitlang/async", "moonbitlang/async/stdio", } supported_targets = "+native" options("is-main": true) - Define (not
async fn main). Spawn concurrent tasks viafn mainfor structured concurrency:with_task_groupmbt///| async fn main { @async.with_task_group(group => { group.spawn_bg(() => { @async.sleep(50) @stdio.stdout.write("A\n") }) group.spawn_bg(() => { @async.sleep(20) @stdio.stdout.write("B\n") }) }) }
Structured-concurrency contract for :
with_task_group- When returns, every task spawned in the group is guaranteed to have terminated — no orphan tasks, no resource leaks.
with_task_group - If any spawned task fails (and was spawned without ), the whole group fails: every other task in the group is cancelled, and the error propagates out of
allow_failure=true.with_task_group - Cancelled tasks are not considered failures; they raise a cancellation error but don't trigger peer cancellation.
Closure syntax for / :
spawn_bgspawn- ✅ — idiomatic; async-ness is inferred from context.
() => { ... } - ✅ — explicit annotation; equivalent to the arrow form.
async fn() { ... } - ⚠️ — triggers
fn() { ... }: "thisWarning [0027] deprecated_syntaxis asynchronous but not annotated withfn". Don't use.async - ❌ ,
async () => ...,fn() async { ... }— all parse errors.fn(args) async { ... }only goes beforeasync, never before an arrow lambda or after a parameter list.fn
异步编程使用编译器支持加上运行时。该运行时对native后端支持最佳,对IO无关API的JavaScript支持有限,目前不支持WebAssembly。异步IO示例优先使用native。使用和探索API。
moonbitlang/asyncmoon add moonbitlang/async@<version>moon ide doc "@async"面向用户的子包:(核心:任务、定时器、取消)、、、、等。
每个都必须在中单独导入。
@async@async/aqueue@async/fs@async/stdio@async/websocketmoon.pkg- 在中添加依赖并指定native目标:
moon.mod.jsonjson{ "deps": { "moonbitlang/async": "0.18.1" }, "preferred-target": "native" } - 在可执行文件的中,设置
moon.pkg,限制为native,并导入所需内容:is-mainimport { "moonbitlang/async", "moonbitlang/async/stdio", } supported_targets = "+native" options("is-main": true) - 定义(而非
async fn main)。通过fn main生成并发任务以实现结构化并发:with_task_groupmbt///| async fn main { @async.with_task_group(group => { group.spawn_bg(() => { @async.sleep(50) @stdio.stdout.write("A\n") }) group.spawn_bg(() => { @async.sleep(20) @stdio.stdout.write("B\n") }) }) }
with_task_group- 当返回时,组中生成的每个任务都保证已终止——无孤儿任务,无资源泄漏。
with_task_group - 如果任何生成的任务失败(且生成时未设置),整个组都会失败:组中其他所有任务都会被取消,错误会传播出
allow_failure=true。with_task_group - 被取消的任务不被视为失败;它们会抛出取消错误,但不会触发对等任务取消。
spawn_bgspawn- ✅ — 符合惯用写法;异步性由上下文推断。
() => { ... } - ✅ — 显式注解;与箭头形式等效。
async fn() { ... } - ⚠️ — 触发
fn() { ... }: "此Warning [0027] deprecated_syntax是异步的但未标注fn"。请勿使用。async - ❌ ,
async () => ...,fn() async { ... }— 均为解析错误。fn(args) async { ... }只能放在async之前,不能放在箭头lambda之前或参数列表之后。fn
Async tests
异步测试
Use for tests that call async functions. The package containing the test must import for the test mode; import any async subpackages used by the test in the same block.
async testmoonbitlang/asyncfor "test"import {
"moonbitlang/async",
"moonbitlang/async/stdio",
} for "test"mbt
///|
async test "sleep completes" {
@async.sleep(1)
inspect("done", content="done")
}- There is no keyword (similar to functions that raise errors). Inside an
await, call async functions normally.async test - Async tests run in parallel by default. Avoid shared ports, files, environment variables, and global mutable state unless each test isolates its resources.
- Run with unless
moon test --target nativesetsmoon.mod.json. Use"preferred-target": "native"when checking test names or async scheduling behavior.moon test -v - In and docstrings,
README.mbt.mdblocks may containmbt checkblocks; make sure the package importsasync testfor the relevant test mode.moonbitlang/async
对于调用异步函数的测试,使用。包含测试的包必须为测试模式导入;在同一个块中导入测试使用的任何异步子包。
async testmoonbitlang/asyncfor "test"import {
"moonbitlang/async",
"moonbitlang/async/stdio",
} for "test"mbt
///|
async test "sleep completes" {
@async.sleep(1)
inspect("done", content="done")
}- 没有关键字(类似抛出错误的函数)。在
await中,正常调用异步函数即可。async test - 异步测试默认并行运行。除非每个测试隔离其资源,否则避免共享端口、文件、环境变量和全局可变状态。
- 运行时使用,除非
moon test --target native设置了moon.mod.json。检查测试名称或异步调度行为时使用"preferred-target": "native"。moon test -v - 在和文档字符串中,
README.mbt.md块可包含mbt check块;确保包为相关测试模式导入了async test。moonbitlang/async
MoonBit Language Tour
MoonBit语言概览
Core facts
核心特性
- Expression‑oriented: ,
if, loops return values; the last expression is the return value.match - References by default: Arrays/Maps/structs mutate via reference; use for primitive mutability.
Ref[T] - 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: is private by default;
fnexposes read/construct as allowed;puballows external construction.pub(all) - Naming convention: lower_snake for values/functions; UpperCamel for types/enums; enum variants start UpperCamel.
- Packages: No in code files; call via
import. Configure imports in@alias.fn.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#, is only needed when you want to reassign a variable, not for mutating fields of a struct or elements of an array/map.
let mut
- 表达式导向: 、
if、循环都返回值;最后一个表达式即为返回值。match - 默认引用: Array/Map/结构体通过引用可变;使用实现基本类型可变。
Ref[T] - 代码块: 使用分隔顶级项。逐块生成代码。 如果需要在代码块(由大括号包裹)内添加空行,在空行后添加注释行(可带或不带注释文本)。
///| - 可见性: 默认私有;
fn允许按规则读取/构造;pub允许外部构造。pub(all) - 命名约定: 值/函数使用小写蛇形命名;类型/枚举使用大驼峰命名;枚举变体以大驼峰开头。
- 包: 代码文件中无;通过
import调用。在@alias.fn中配置导入。moon.pkg - 占位符: 是MoonBit代码中有效的未实现占位符。
... - 全局值: 默认不可变,通常需要类型注解。
- 垃圾回收: MoonBit有GC,无需生命周期注解,无所有权系统。
与Rust不同,类似F#,仅在你想重新赋值变量时需要,而不是为了修改结构体字段或数组/Map的元素。
let mut
MoonBit Error Handling (Checked Errors)
MoonBit错误处理(检查型错误)
MoonBit uses checked error-throwing functions, not unchecked exceptions. All errors are a subtype of and you can declare your own error types using .
Use in signatures to declare error types and let errors propagate by
default.
to handle errors explicitly. Use to abort if it does raise. Occasionally, use to convert to in tests for inspection.
Errorsuberrorraisetry { } catch { }try!try?Result[...]mbt
///|
/// Declare error types with 'suberror'
suberror ValueError {
ValueError(String)
}
///|
/// Tuple struct to hold position info
struct Position(Int, Int) derive(ToJson, Debug, 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, Debug)
///|
/// 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::Failure(msg)) && msg.contains("Division by zero") else {
fail("Expected error")
}
}
// Three ways to handle errors:
///|
/// Propagate automatically
fn use_parse(s : String, position~ : Position) -> Int raise ParseError {
let x = parse_int(s, 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
}
///|
/// Use try! to abort if it raises, no raise in the signature
fn use_parse2(position~ : Position) -> Int {
let x = try! parse_int("123", position~) // label punning
x * 2
}
///|
/// Handle with try-catch
fn handle_parse(s : String, position~ : Position) -> Int {
parse_int(s, position~) catch {
ParseError::InvalidEof(pos=_) => {
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 functions automatically can raise errors without explicitly stating this.
asyncMoonBit使用检查型抛错函数,而非未检查异常。所有错误都是的子类型,你可以使用声明自己的错误类型。
在签名中使用声明错误类型,默认让错误传播。使用显式处理错误。使用在出错时中止。偶尔在测试中使用转换为进行检查。
Errorsuberrorraisetry { } catch { }try!try?Result[...]mbt
///|
/// 使用'suberror'声明错误类型
suberror ValueError {
ValueError(String)
}
///|
/// 保存位置信息的元组结构体
struct Position(Int, Int) derive(ToJson, Debug, 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, Debug)
///|
/// 函数声明可能抛出的错误
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 "检查抛错函数" {
let result : Result[Int, Error] = try? div(1, 0)
guard result is Err(Failure::Failure(msg)) && msg.contains("Division by zero") else {
fail("Expected error")
}
}
// 三种错误处理方式:
///|
/// 自动传播错误
fn use_parse(s : String, position~ : Position) -> Int raise ParseError {
let x = parse_int(s, position~) // 标签简写,等效于position=position
// 错误默认自动传播。
// 与Swift不同,你不需要为可能抛错的函数标记`try`;编译器会自动推断。这使错误处理既显式又简洁。
x * 2
}
///|
/// 使用try!在出错时中止,签名中无raise
fn use_parse2(position~ : Position) -> Int {
let x = try! parse_int("123", position~) // 标签简写
x * 2
}
///|
/// 使用try-catch处理错误
fn handle_parse(s : String, position~ : Position) -> Int {
parse_int(s, position~) catch {
ParseError::InvalidEof(pos=_) => {
println("Parse failed: InvalidEof")
-1 // 默认值
}
_ => 2
}
}重要提示:调用可能抛错的函数时,如果你只想传播错误,不需要任何标记;编译器会自动推断。
注意所有函数默认可以抛错,无需显式声明。
asyncInteger, Char and overloaded literals
整数、字符和重载字面量
MoonBit supports , , , , , , , etc.
When the type is known, the literal can be overloaded:
ByteInt16IntUInt16UIntInt64UInt64mbt
///|
test "integer and char literal overloading disambiguation via type in the current context" {
let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
1, 1, 1, 1, 1,
)
// The literal `1` is overloaded based on the expected type in the current context.
// compile time error if the literal cannot be represented in the target type,
// e.g. let a7 : Byte = 256 // ❌ won't compile, 256 exceeds Byte max value 255
assert_eq(int, uint16.to_int())
let (a1, a2, a3) : (Int, Char, UInt16) = ('b', 'b', 'b')
// char literal overloading, `a1` will be the unicode value of 'b',
// compile time error when the literal cannot be represented in the target type
// e.g, let a6 : UInt16 = '𐍈' // ❌ won't compile, '𐍈' is U+10348, which exceeds UInt16 max value 0xffff
let a4 : Byte = b'b' // Byte literal
}MoonBit支持、、、、、、等类型。
当类型已知时,字面量可以重载:
ByteInt16IntUInt16UIntInt64UInt64mbt
///|
test "整数和字符字面量通过上下文类型消除歧义" {
let (int, uint, uint16, int64, byte) : (Int, UInt, UInt16, Int64, Byte) = (
1, 1, 1, 1, 1,
)
// 字面量`1`根据当前上下文的预期类型重载。
// 如果字面量无法在目标类型中表示,会触发编译错误,
// 例如let a7 : Byte = 256 // ❌ 无法编译,256超过Byte的最大值255
assert_eq(int, uint16.to_int())
let (a1, a2, a3) : (Int, Char, UInt16) = ('b', 'b', 'b')
// 字符字面量重载,`a1`将是'b'的unicode值,
// 当字面量无法在目标类型中表示时触发编译错误
// 例如let a6 : UInt16 = '𐍈' // ❌ 无法编译,'𐍈'是U+10348,超过UInt16的最大值0xffff
let a4 : Byte = b'b' // Byte字面量
}Bytes (Immutable)
Bytes(不可变)
mbt
///|
test "bytes literals" {
let b0 : Bytes = b"abcd"
let b1 : 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字面量" {
let b0 : Bytes = b"abcd"
let b1 : Bytes = [0xff, 0x00, 0x01] // 数组字面量重载
guard b0 is [b'a', ..] && b0[1] is b'b' else {
// Bytes可以作为BytesView进行模式匹配和索引
fail("unexpected bytes content")
}
}Array (Resizable)
Array(可调整大小)
mbt
///|
test "array literals overloading: disambiguation via type in the current context" {
let (a0, a1, a2, a3) : (
Array[Int],
FixedArray[Int],
ReadOnlyArray[Int],
ArrayView[Int],
) = ([1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3])
// The literal `[1, 2, 3]` is overloaded based on the expected type in the current context.
// Defaults to Array[_]
}mbt
///|
test "数组字面量重载:通过上下文类型消除歧义" {
let (a0, a1, a2, a3) : (
Array[Int],
FixedArray[Int],
ReadOnlyArray[Int],
ArrayView[Int],
) = ([1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3])
// 字面量`[1, 2, 3]`根据当前上下文的预期类型重载。
// 默认是Array[_]
}String (Immutable UTF-16)
String(不可变UTF-16)
s[i]s.get_char(i)Char?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]s.get_char(i)Char?mbt
///|
test "字符串索引和utf8编码/解码" {
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
字符串插值 && StringBuilder
MoonBit uses for string interpolation, for custom types, they need to implement trait .
\{}Showmbt
///|
test "string interpolation basics" {
let name : String = "Moon"
let config = { "cache": 123 }
let version = 1.0
println("Hello \{name} v\{version}") // "Hello Moon v1"
// ❌ 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()
sb.write_char('[')
sb.write_view([ for x in [1, 2, 3] => "\{x}" ].join(","))
sb.write_char(']')
inspect(sb, content="[1,2,3]")
let x = 42
let streamed = StringBuilder()
streamed <+ "hello \{x}"
inspect(streamed, content="hello 42")
}Expressions inside can only be basic expressions (no quotes, newlines, or nested interpolations). String literals are not allowed as they make lexing too difficult.
\{}String interpolation can also be streamed directly into a /-style writer with :
LoggerStringBuilder<+mbt
writer <+ "hello \{x}"This expands to calls on the writer:
mbt
writer.write_string("hello ")
writer.write(x)Literal string segments use ; interpolated expressions use .
The expansion is macro-style: it depends on how the type implements the and methods. Types such as HTMLBuilder or JSONBuilder can support interpolation and streaming with the same syntax but different semantics.
Because MoonBit allows local methods on foreign types, a package can adapt an existing writer type to this syntax by adding local and methods.
write_stringwritewriterwrite_stringwritewrite_stringwriteMoonBit使用进行字符串插值,自定义类型需要实现 trait。
\{}Showmbt
///|
test "字符串插值基础" {
let name : String = "Moon"
let config = { "cache": 123 }
let version = 1.0
println("Hello \{name} v\{version}") // "Hello Moon v1"
// ❌ 错误 - 插值内不允许引号:
// println(" - Checking if 'cache' section exists: \{config["cache"]}")
// ✅ 正确 - 先提取到变量:
let has_key = config["cache"] // 插值内不允许`"`
println(" - Checking if 'cache' section exists: \{has_key}")
let sb = StringBuilder()
sb.write_char('[')
sb.write_view([ for x in [1, 2, 3] => "\{x}" ].join(","))
sb.write_char(']')
inspect(sb, content="[1,2,3]")
let x = 42
let streamed = StringBuilder()
streamed <+ "hello \{x}"
inspect(streamed, content="hello 42")
}\{}字符串插值也可以通过直接流式写入/风格的写入器:
<+LoggerStringBuildermbt
writer <+ "hello \{x}"这会展开为对写入器的调用:
mbt
writer.write_string("hello ")
writer.write(x)字面字符串段使用;插值表达式使用。
展开是宏风格的:取决于类型如何实现和方法。HTMLBuilder或JSONBuilder等类型可以支持相同语法但不同语义的插值和流式写入。
由于MoonBit允许在外部类型上定义本地方法,包可以通过添加本地和方法来适配现有写入器类型。
write_stringwritewriterwrite_stringwritewrite_stringwriteMultiple 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 "多行字符串字面量" {
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)
Map(可变,保留插入顺序)
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([])
// 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字面量和常见操作" {
// Map字面量语法
let map : Map[String, Int] = { "a": 1, "b": 2, "c": 3 }
let empty : Map[String, Int] = {} // 空map,推荐写法
let also_empty : Map[String, Int] = Map([])
// 从键值对数组创建
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 (, , ) 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 , , , they also take (implicit conversion).
StringViewBytesViewArrayView[T][:]StringBytesArray*View- →
StringviaStringViewors[:]ors[start:end]ors[start:]s[:end] - →
BytesviaBytesVieworb[:], etc.b[start:end] - ,
Array[T],FixedArray[T]ArrayView[T]ReadOnlyArray[T] →a[:]viaa[start:end]`, etc.or
Important: StringView slice is slightly different due to unicode safety:
may raise an error at surrogate boundaries (UTF-16 encoding edge case). You have two options:
s[a:b]- Use if you're certain the boundaries are valid (crashes on invalid boundaries)
try! s[a:b] - 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 , , or when you need ownership. ()
.to_string().to_bytes().to_array()moon ide doc StringView核心概念: 视图类型(、、)是零拷贝、非拥有的只读切片,通过语法创建。它们不分配内存,非常适合传递子序列而无需复制数据,对于接受、、的函数,也接受对应的(隐式转换)。
StringViewBytesViewArrayView[T][:]StringBytesArray*View- →
String通过StringView或s[:]或s[start:end]或s[start:]s[:end] - →
Bytes通过BytesView或b[:]等b[start:end] - 、
Array[T]、FixedArray[T]ArrayView[T]ReadOnlyArray[T] →a[:]通过a[start:end]`等或
重要提示: StringView切片由于unicode安全性略有不同:
在代理边界(UTF-16编码边缘情况)可能抛出错误。你有两种选择:
s[a:b]- 如果你确定边界有效,使用(边界无效时崩溃)
try! s[a:b] - 让错误传播给调用者进行适当处理
何时使用视图:
- 使用剩余模式进行模式匹配()
[first, .. rest] - 将切片传递给函数而无分配开销
- 避免大序列的不必要复制
当你需要所有权时,使用、或转换回去。()
.to_string().to_bytes().to_array()moon ide doc StringViewUser defined types(enum
, struct
)
enumstruct用户定义类型(enum
、struct
)
enumstructmbt
///|
enum Tree[T] {
Leaf(T) // Unlike Rust, no comma here
Node(left~ : Tree[T], T, right~ : Tree[T]) // enum can use labels
} derive(Debug, 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(Debug, ToJson) // derive traits for Point
///|
pub fn Point::Point(x~ : Int, y~ : Int) -> Point {
{ x, y }
}
///|
test "user defined types: enum and struct" {
json_inspect(Point(x=10, y=20), content={ "x": 10, "y": 20 })
debug_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(Debug, 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(Debug, ToJson) // 为Point派生trait
///|
pub fn Point::Point(x~ : Int, y~ : Int) -> Point {
{ x, y }
}
///|
test "用户定义类型:enum和struct" {
json_inspect(Point(x=10, y=20), content={ "x": 10, "y": 20 })
debug_inspect(
Point(x=10, y=20),
content=(
#|{ x: 10, y: 20 }
),
)
}Functional for
loop
for函数式for
循环
formbt
///|
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
//} nobreak { // 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
}
} nobreak { // exit of for loop
if i < len && arr[i] == value {
Ok(i)
} else {
Err(i)
}
} where {
proof_invariant: 0 <= i && i <= j && j <= len,
proof_invariant: i == 0 || arr[i - 1] < value,
proof_invariant: j == len || arr[j] >= value,
proof_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]
debug_inspect(binary_search(arr, 5), content="Ok(2)") // Array to ArrayView implicit conversion when passing as arguments
debug_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 loops instead of imperative loops
WHENEVER POSSIBLE, as they are easier to read and reason about.
formbt
///|
pub fn binary_search(arr : ArrayView[Int], value : Int) -> Result[Int, Int] {
let len = arr.length()
// 函数式for循环:
// 初始状态 ; [条件] ; [更新] {
// 循环体使用`continue`更新状态
//} nobreak { // 退出块
// }
// 条件和更新是可选的
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 // 函数式更新循环状态
}
} nobreak { // for循环退出
if i < len && arr[i] == value {
Ok(i)
} else {
Err(i)
}
} where {
proof_invariant: 0 <= i && i <= j && j <= len,
proof_invariant: i == 0 || arr[i - 1] < value,
proof_invariant: j == len || arr[j] >= value,
proof_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 "函数式for循环控制流" {
let arr : Array[Int] = [1, 3, 5, 7, 9]
debug_inspect(binary_search(arr, 5), content="Ok(2)") // 传递参数时Array隐式转换为ArrayView
debug_inspect(binary_search(arr, 6), content="Err(3)")
// 也支持for迭代
for i, v in arr {
println("\{i}: \{v}") // `i`是索引,`v`是值
}
}强烈建议尽可能使用函数式循环而非命令式循环,因为它们更易读和推理。
forLoop Invariants with where
Clause
where带where
子句的循环不变量
whereThe clause attaches machine-checkable invariants and human-readable reasoning to functional loops. This enables formal verification thinking while keeping the code executable. Note for trivial loops, you are encouraged to convert it into so no reasoning is needed.
whereforfor .. inSyntax:
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:
-
Make invariants checkable: Invariants must be valid MoonBit boolean expressions using loop variables and captured values.
-
Use boundary witnesses: For properties over ranges (e.g., "all elements in arr[0..i) satisfy P"), check only boundary elements. For sorted arrays,implies all
arr[i-1] < value.arr[0..i) < value -
Handle edge cases with: Use patterns like
||to handle boundary conditions where the check would be out of bounds.i == 0 || arr[i-1] < value -
Cover three aspects in reasoning:
- Preservation: Why each maintains the invariants
continue - Termination: Why the loop eventually exits (e.g., a decreasing measure)
- Correctness: Why the invariants at exit imply the desired postcondition
- Preservation: Why each
whereforfor .. in语法:
mbt
for ... {
...
} where {
invariant : <boolean_expr>, // 在调试构建中运行时检查
invariant : <boolean_expr>, // 允许多个不变量
reasoning : <string> // 证明草图的文档
}编写良好的不变量:
-
使不变量可检查: 不变量必须是使用循环变量和捕获值的有效MoonBit布尔表达式。
-
使用边界证据: 对于范围属性(例如"arr[0..i)中的所有元素满足P"),仅检查边界元素。对于排序数组,意味着所有
arr[i-1] < value。arr[0..i) < value -
使用处理边缘情况: 使用
||等模式处理检查会越界的边界条件。i == 0 || arr[i-1] < value -
在推理中涵盖三个方面:
- 保留性: 为什么每个都能保持不变量
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
// `to_repr` (from the prelude `Debug` trait) renders Option via the
// non-deprecated `Show for Repr`, avoiding the deprecated `Show for Option`.
"\{positional},\{required},\{to_repr(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: is not an optional parameter.
Callers still must pass it (as /).
arg : Type?NoneSome(...)mbt
///|
fn with_config(a : Int?, b : Int?, c : Int) -> String {
"\{to_repr(a)},\{to_repr(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: (no default => double Option).
If you want a defaulted optional parameter, write , not .
arg? : Type?b? : Int = 1b? : 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: (use labeled optional parameters instead)
arg : APIOptionsmbt
///|
/// 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
// `to_repr`(来自前置`Debug` trait)通过非弃用的`Show for Repr`渲染Option,避免弃用的`Show for Option`。
"\{positional},\{required},\{to_repr(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?NoneSome(...)mbt
///|
fn with_config(a : Int?, b : Int?, c : Int) -> String {
"\{to_repr(a)},\{to_repr(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")
}反模式:(无默认值 => 双重Option)。
如果你想要带默认值的可选参数,编写,而非。
arg? : Type?b? : Int = 1b? : 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 : APIOptionsmbt
///|
/// 不要使用结构体来分组选项。
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