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.json/moon.pkgfiles (package boundaries and imports).moon.pkg.json
- 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 - Use when old APIs should warn and be removed after migration.
#deprecated - Use when temporary backward compatibility is required during migration.
#alias(old_api, deprecated) - Remove and
#deprecatedshims once callers are migrated and warnings are gone.#alias
- 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.
moon check - 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文件(包边界与导入配置)。moon.pkg.json
- 找到
-
编码前先探索 API
- 优先使用 命令查询现有函数/类型/方法,再考虑添加新代码。
moon ide doc - 使用 、
moon ide outline和moon ide peek-def进行语义导航。moon ide find-references
- 优先使用
-
可靠的重构操作
- 使用 进行语义重构。若多个符号同名,添加
moon ide rename参数指定位置。--loc filename:line:col - 当旧 API 需要发出警告并在迁移后移除时,使用 标记。
#deprecated - 迁移期间需临时保持向后兼容时,使用 。
#alias(old_api, deprecated) - 当所有调用方完成迁移且警告消失后,移除 和
#deprecated兼容代码。#alias
- 使用
-
最小化修改并保持包内操作
- 将修改限制在对应包内,使用 顶级分隔符,将代码拆分为高内聚的文件。
///|
- 将修改限制在对应包内,使用
-
循环验证修改
- 编辑后运行 。
moon check - 使用 运行针对性测试,快照变更时使用
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 or (legacy mode). Subdirectories may also contain
files indicating that a different set of dependencies can be used for that subdir.
.mbt.mbtimoon.mod.jsonmoon.pkgmoon.pkg.jsonmoon.mod.jsonMoonBit 使用 作为源代码文件扩展名, 作为接口文件扩展名。MoonBit 项目的顶层目录下有一个 文件,用于指定项目元数据。项目可包含多个包,每个包都有自己的 或 (旧版格式)。子目录中也可能包含 文件,表示该子目录可使用独立的依赖集。
.mbt.mbtimoon.mod.jsonmoon.pkgmoon.pkg.jsonmoon.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 a(or
moon.pkg) file in each directory. All subcommands ofmoon.pkg.jsonwill 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.pkg.json子命令仍在模块目录(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代码示例的mbt check文件,也可将README.mbt.md软链接到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: Older code may use or
function_name!(...)- these are deprecated; use normal calls andfunction_name(...)?for Result conversiontry? - 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 ...
- 变量/函数名不要使用大写 - 会导致编译错误
- 可变记录字段不要忘记加 - 默认不可变(注意:数组通常不需要
mut,除非完全重新赋值变量,例如简单的 push 操作不需要mut)mut - 不要忽略错误处理 - 必须显式处理错误
- 不要不必要地使用 - 最后一个表达式即为返回值
return - 方法定义不要省略 Type:: 前缀 - 方法需要显式的类型前缀
- 不要忘记处理数组边界 - 使用 进行安全访问
get() - 调用其他包的函数时不要忘记加 @package 前缀
- 不要使用 ++ 或 --(不支持) - 使用 或
i = i + 1i += 1 - 不要为会抛出错误的函数添加显式 - 错误会自动传播(与 Swift 不同)
try - 旧版语法:旧代码可能使用 或
function_name!(...)- 这些已被弃用;请使用普通调用和function_name(...)?进行 Result 转换try? - 优先使用范围 循环而非 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 - - 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 --target all - - Add dependency
moon add package - - Remove dependency
moon remove package - - Format code - should be run periodically - note that the files may be rewritten Note you can also use
moon fmtto run commands in a specific directory.moon -C dir check
- - 创建新项目
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
测试命令
- - 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) for YourType。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标记: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]moon ide [doc|peek-def|outline|find-references|hover|rename]
用于代码导航和重构
moon ide [doc|peek-def|outline|find-references|hover|rename]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. These tools save tokens and are more precise than grepping (--locdisplays 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]参数。这些工具比 grep 更节省时间且更精确(grep 会同时显示定义、调用位置和注释中的结果)。--loc
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
-
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"
- 显示包
-
通配符匹配:使用通配符进行部分匹配,例如
*查找所有名称包含 "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
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]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()
| }当用户询问:"能否检查 的实现是否正确?"
可运行 获取定义上下文(这比 grep 更好,因为它会通过语义搜索整个项目):
Parser::read_u32_leb128moon ide peek-def Parser::read_u32_leb128file
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 | ///|
| /// 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>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.goto-definition - 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 - 查找当前模块中符号的所有引用
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 # 更新包索引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 optionsor 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 or file are not recognized.
moon.pkgmoon.pkg.json简洁版 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.pkgmoon.pkg.jsonPackage 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.mdMoonBit 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 - 默认引用语义:数组/映射/结构体通过引用可变;使用 实现基本类型的可变性。
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 and you can declare your own error types using .
Use in signatures to declare error types and let errors propagate by
default. Use to convert to in tests, or
to handle errors explicitly. Use to abort if it does raise.
Errorsuberrorraisetry?Result[...]try { } catch { }try!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 functions automatically can raise errors without explicitly stating this.
asyncMoonBit 使用检查型抛出错误的函数,而非未检查的异常。所有错误均为 的子类型,可使用 声明自定义错误类型。在函数签名中使用 声明错误类型,错误默认自动传播。在测试中使用 转换为 ,或使用 显式处理错误。使用 在错误发生时终止程序。
Errorsuberrorraisetry?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
}
}重要提示:调用可能抛出错误的函数时,若仅需传播错误,无需添加任何标记;编译器会自动推断。注意:所有 函数默认可抛出错误,无需显式声明。
asyncIntegers, Char
整数、字符
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 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 支持 、、、、、、 等类型。当类型已知时,字面量会被重载:
ByteInt16IntUInt16UIntInt64UInt64mbt
///|
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]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 "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 .
\{}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.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 使用 进行字符串插值,自定义类型需要实现 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.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 (, , ) 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]→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 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(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函数式 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
//} 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 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` 更新状态
//} 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` 是值
}
}强烈建议:只要可能,就使用函数式 循环而非命令式循环,因为函数式循环更易读和推理。
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
"\{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: 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 {
"\{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: (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
"\{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?NoneSome(...)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")
}反模式:(无默认值 => 双重 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