moonbit-bestpractice
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMoonBit Coding Standards
MoonBit编码标准
1. Documentation
1. 文档规范
- Use for documentation comments on top-level definitions (functions, structs, enums, traits, tests). This is the output format of
///|.moon fmt - Ensure public APIs are documented.
- 对顶层定义(函数、结构体、枚举、Trait、测试)使用编写文档注释,这是
///|的输出格式。moon fmt - 确保公开API都有文档说明。
2. Naming Conventions
2. 命名约定
- Types (Structs, Enums) & Traits: PascalCase (e.g., ,
Position,GeoJSONObject).ToBBox - Abbreviations: Abbreviations other than those at the beginning (such as JSON or ID) should generally be all uppercase (e.g. ,
FromJSON).JSONToMap - Functions & Methods: snake_case (e.g., ,
from_json,to_geometry).boolean_point_in_polygon - Variables & Parameters: snake_case (e.g., ,
coordinates).shifted_poly - No Abbreviations: Do not abbreviate variable names unless they are common abbreviations (e.g., ,
id). Avoidjsonforp,pointforls, etc.line_string - Collections: Use the suffix for array arguments/variables instead of plural names (e.g.,
_arrayinstead ofpolygon_array).polygons - Constructors: Always define inside the struct body. Factory functions use
fn new/new_hogenaming — neverfrom_hoge.::new
- 类型(结构体、枚举)与Trait:采用PascalCase命名法(例如:、
Position、GeoJSONObject)。ToBBox - 缩写规则:除开头的缩写(如JSON或ID)外,其他缩写应全部大写(例如、
FromJSON)。JSONToMap - 函数与方法:采用snake_case命名法(例如:、
from_json、to_geometry)。boolean_point_in_polygon - 变量与参数:采用snake_case命名法(例如:、
coordinates)。shifted_poly - 禁止随意缩写:除非是通用缩写(如、
id),否则不要缩写变量名。避免用json代替p、point代替ls等。line_string - 集合命名:数组类型的参数/变量使用后缀,而非复数形式(例如:
_array而非polygon_array)。polygons - 构造函数:始终在结构体内部定义。工厂函数使用
fn new/new_hoge命名——绝不要使用from_hoge。::new
3. Idioms & Best Practices
3. 编码惯例与最佳实践
3.1 Constructors & Instance Initialization
3.1 构造函数与实例初始化
-
Default constructor: Thedeclaration inside the struct body is the constructor definition itself — do NOT write a separate
fn newimplementation outside.fn StructName::new(...)supportsfn new, so validation logic should be placed inraiseto ensure instances are always in a valid state.newOK:mbtstruct MyStruct { x : Int y : Int fn new(x~ : Int, y~ : Int) -> MyStruct }NG:mbtfn MyStruct::new(x~ : Int, y~ : Int) -> MyStruct { { x, y } } -
Factory functions: Define separate static functions with names like,
new_hoge, etc. Never name themfrom_hoge. Factory functions must generate values via the::newconstructor (newis equivalent toStructName(...)):StructName::new(...)mbtstruct Rect { x : Double y : Double width : Double height : Double // Validation in new ensures all Rect instances have valid size fn new(x~ : Double, y~ : Double, width~ : Double, height~ : Double) -> Rect raise } // Conversion: create from a different representation fn Rect::from_corners(x1~ : Double, y1~ : Double, x2~ : Double, y2~ : Double) -> Rect { Rect(x=x1, y=y1, width=x2 - x1, height=y2 - y1) } // Specific state: create a type with optional fields in a predetermined state fn Rect::new_unit(x~ : Double, y~ : Double) -> Rect { Rect(x~, y~, width=1.0, height=1.0) } -
Initialization: Struct literal syntax () should ONLY be used strictly within constructor functions like
StructName::{...}. External code must use the constructor syntax (new).StructName(...) -
Updating: Use dedicated update functions/methods to modify values.
-
Struct Update Syntax: Avoid using Struct Update Syntax (e.g.,) whenever possible, as it may bypass validation logic or constraints.
{ ..base, field: value } -
Ignore Usage: Use proper pipeline style when ignoring return values:.
expr |> ignore
-
默认构造函数:结构体内部声明的即为构造函数定义——不要在外部单独实现
fn new。fn StructName::new(...)支持fn new特性,因此验证逻辑应放在raise中,确保实例始终处于有效状态。new正确示例:mbtstruct MyStruct { x : Int y : Int fn new(x~ : Int, y~ : Int) -> MyStruct }错误示例:mbtfn MyStruct::new(x~ : Int, y~ : Int) -> MyStruct { { x, y } } -
工厂函数:定义独立的静态函数,命名如、
new_hoge等,绝不要命名为from_hoge。工厂函数必须通过::new构造函数生成实例(new等价于StructName(...))StructName::new(...)mbtstruct Rect { x : Double y : Double width : Double height : Double // new中的验证逻辑确保所有Rect实例的尺寸都合法 fn new(x~ : Double, y~ : Double, width~ : Double, height~ : Double) -> Rect raise } // 转换:从不同表示形式创建实例 fn Rect::from_corners(x1~ : Double, y1~ : Double, x2~ : Double, y2~ : Double) -> Rect { Rect(x=x1, y=y_1, width=x_ - x1, height=y2 - y1) } // 指定状态:创建具有可选字段且处于预设状态的实例 fn Rect::new_unit(x~ : Double, y~ : Double) -> Rect { Rect(x~, y~, width=1.0, height=1.0) } -
初始化规则结构体字面量语法()仅能在
StructName::{...}这类构造函数内部严格使用。外部代码必须使用构造函数语法(new)。 更新规则**:使用专门的更新函数方法修改值。StructName(...) -
Struct Update Syntax:尽可能避免使用Struct Update Syntax(例如),因为它可能会绕过验证逻辑或约束条件。 忽略返回值**:忽略返回值时使用标准管道风格:
{ ..base, field value }。expr |> ignore
3.2 Error Handling
3.2 错误处理
Use the effect for functions that can fail instead of returning types for synchronous logic.
raiseResultDefining error types:
mbt
suberror DivError { DivError(String) }
suberror E3 {
A
B(String)
C(Int, loc~ : SourceLoc)
}suberrorsuberror A Bsuberror A { A(B) }Raising errors:
- Custom error:
raise DivError("division by zero") - Generic failure: — convenience function that raises
fail("message")type with source locationFailure
Function signatures:
mbt
fn div(x : Int, y : Int) -> Int raise DivError { ... }
fn f() -> Unit raise { ... }
fn add(a : Int, b : Int) -> Int noraise { a + b }- : function may raise a specific error type
raise CustomError - or
raise: function may raise any errorraise Error - : function guaranteed not to raise
noraise
Handling errors:
mbt
try div(42, 0) catch {
DivError(msg) => println(msg)
} noraise {
v => println(v)
}
let a = div(42, 0) catch { _ => 0 }
let res = try? (div(6, 0) * div(6, 3))
try! div(42, 0)- : full error handling
try { expr } catch { pattern => handler } noraise { v => ... } - : simplified inline catch
let a = expr catch { _ => default } - : convert to
try? exprResult[T, Error] - : panic on error
try! expr
Error polymorphism:
Use for higher-order functions that conditionally throw:
raise?mbt
fn[T] map(
array : Array[T],
f : (T) -> T raise?
) -> Array[T] raise? { ... }When is , is also . When raises, raises.
fnoraisemapnoraisefmapBest practices:
- Prefer effect over
raisefor synchronous codeResult - In tests, let errors propagate or use to assert success
guard
对于可能失败的同步逻辑函数,使用特性而非返回类型。
raiseResult定义错误类型:
mbt
suberror DivError { DivError(String) }
suberror E3 {
A
B(String)
C(Int, loc~ : SourceLoc)
}suberrorsuberror A Bsuberror A { A(B) }抛出错误:
- 自定义错误:
raise DivError("division by zero") - 通用失败:——便捷函数,会抛出带源码位置的
fail("message")类型错误Failure
函数签名:
mbt
fn div(x : Int, y : Int) -> Int raise DivError { ... }
fn f() -> Unit raise { ... }
fn add(a : Int, b : Int) -> Int noraise { a + b }- :函数可能抛出特定错误类型
raise CustomError - 或
raise:函数可能抛出任意错误raise Error - :函数保证不会抛出错误
noraise
处理错误:
mbt
try div(42, 0) catch {
DivError(msg) => println(msg)
} noraise {
v => println(v)
}
let a = div(42, 0) catch { _ => 0 }
let res = try? (div(6, 0) * div(6, 3))
try! div(42, 0)- :完整错误处理
try { expr } catch { pattern => handler } noraise { v => ... } - :简化内联捕获
let a = expr catch { _ => default } - :转换为
try? expr类型Result[T, Error] - :错误时触发panic
try! expr
错误多态性:
对于可能条件性抛出错误的高阶函数,使用:
raise?mbt
fn[T] map(
array Array[T],
f : (T) -> T raise?
) -> Array[T] raise? { ... }当为时,也为;当会抛出错误时,也会抛出错误。
fnoraisemapnoraisefmap最佳实践:
- 同步代码优先使用特性而非
raise类型Result - 测试中,可让错误传播或使用断言成功
guard
3.3 Pattern Matching & Guards
3.3 模式匹配与Guard
-
Avoidfor Validation: Use
ifinstead:guardmbtguard array.length() > 0 else { raise Error("Empty") } -
Usefor assertions, early returns, or unwrapping:
guard-
General Code: Always provide an explicit fallback using:
elsembtguard hoge is Hoge(fuga) else { raise fail("Message") } -
Tests: Usewithout fallback for better readability:
guardmbtguard feature.geometry is Some(@geojson.Geometry::Polygon(poly))
-
-
Usefor exhaustive handling of Enums.
match -
Use Labeled Arguments/Punners () in patterns and constructors when variable names match field names:
~mbtmatch geometry { Polygon(coordinates~) => coordinates }
-
避免用if做验证:改用:
guardmbtguard array.length() > 0 else { raise Error("Empty") } -
使用进行断言、提前返回或解包:
guard-
通用代码:始终通过提供显式回退逻辑:
elsembtguard hoge is Hoge(fuga) else { raise fail("Message") } -
测试代码:不带回退的可读性更好:
guardmbtguard feature.geometry is Some(@geojson.Geometry::Polygon(poly))
-
-
使用对枚举进行穷尽处理
match -
当变量名与字段名匹配时,在模式和构造函数中使用带标签参数/Punners():
~mbtmatch geometry { Polygon(coordinates~) => coordinates }
3.4 Functions
3.4 函数
-
Anonymous Functions: Prefer arrow syntaxover
args => bodykeywordfn.fn(args) { body } -
Localannotation: Local
fndefinitions must explicitly annotatefn/raiseeffects. Inference for localasyncis deprecated. Arrow functions are unaffected.fnmbtfn outer() -> Unit raise { fn local_fn() -> Unit raise { fail("err") } let arrow_fn = () => { fail("err") } }
-
匿名函数:优先使用箭头语法而非
args => body关键字形式fn。fn(args) { body } -
局部fn注解:局部定义必须显式标注
fn/raise特性。局部async的特性推断已被废弃,箭头函数不受此影响。fnmbtfn outer() -> Unit raise { fn local_fn() -> Unit raise { fail("err") } let arrow_fn = () => fail("err") } }
3.5 Structs & Enums
3.5 结构体与枚举
-
Enum Wrapping Structs: Define independent Structs for each variant, then wrap them in the Enum. Use the same name for the Variant and the Struct.mbt
pub struct Point { ... } pub enum Geometry { Point(Point) MultiPoint(MultiPoint) } derive(Debug, Eq) -
Delegation: When implementing traits for such Enums, pattern match onand delegate to the inner struct's method.
self -
Prefer distinct Structs for complex data wrapped in Enums if polymorphic behavior is needed.
-
Standard traits:,
Debug,Eq,Compare,ToJson,FromJson(note: types containingHashcannot deriveJson).Hash -
trait: Always derive
Debuginstead ofDebug.ShowreplacesDebugas the standard trait for structural formatting. UsesShowfor output.debug_inspect()mbtstruct MyStruct { ... } derive(Debug, Eq) -
Constructor qualified names: Built-in constructors require qualified names (e.g.,). Constructors with arguments cannot be used as higher-order functions; use a wrapper:
Failure::Failurembtlet f = x => Some(x)
-
枚举包裹结构体:为每个枚举变体定义独立的结构体,然后将其包裹在枚举中。变体与结构体使用相同名称。mbt
pub struct Point { ... } pub enum Geometry { Point(Point) MultiPoint(MultiPoint) } derive(Debug, Eq) -
委托实现:为此类枚举实现trait时,对进行模式匹配,然后委托给内部结构体的方法。
self -
如果需要多态行为,优先为枚举中包裹的复杂数据定义独立结构体。
-
标准Trait:、
Debug、Eq、Compare、ToJson、FromJson(注意:包含Hash的类型无法派生Json)。Hash -
Debug Trait:始终派生而非
Debug。Show已取代Debug成为结构格式化的标准Trait,使用Show输出内容。debug_inspect()mbtstruct MyStruct { ... } derive(Debug, Eq) -
构造函数限定:内置构造函数需要限定名(例如:)。带参数的构造函数无法作为高阶函数使用,需使用包装器:
Failure::Failurembtundefined
let f = x => Some(x)
undefined3.6 Traits
3.Trait
-
Definition Notation:mbt
pub trait ToGeoJSON { to_geojson(Self) -> GeoJSON } -
Performance Overrides: Always override default trait methods if a more efficient implementation is possible for the specific type.
-
Implementation Rules:
- Default Implementation: If a method's return value is not and it can be implemented solely using other methods, provide a default implementation.
Self - Trait Object Delegation: When implementing a super-trait for a type that also implements a sub-trait, define the logic as a static function on the sub-trait's object and relay to it.
- Direct Implementation: If delegation is not possible or creates circular dependencies, implement the method directly on the type.
- Default Implementation: If a method's return value is not
-
定义语法mbt
pub trait ToGeoJSON { to_geojson(Self) -> GeoJSON }
性能优化重写:如果针对特定类型有更高效实现方式,务必重写Trait的默认方法。
- 实现规则:
- 默认实现如果方法的返回值不是,且仅能通过方法实现,则提供默认实现。 Trait对象委托**:当为某个类型实现父Trait,而该类型同时实现Trait时,将逻辑定义为子Trait对象的静态函数并委托给它。 直接实现**:如果无法委托或会产生循环依赖,则直接在类型上实现方法。
Self
- 默认实现如果方法的返回值不是
3.7 Cascade Operator
3.7 级联运算符
x..f(){ x.f(); x }Unitmbt
let result = StringBuilder::new()
..write_char('a')
..write_object(1001)
..write_string("abcdef")
.to_string()Enables chaining mutable operations without modifying return types. Compiler warns if result is ignored.
x..f(){ x.f(); x }Unitmbt
let result = StringBuildernew()
..write_char('a')
..write_object(10)
..write_string("abcdef")
.to_string()支持在不修改返回类型的情况下链式调用可变操作。如果忽略返回值,编译器会发出警告。
3.8 Range Syntax
3.8 范围语法
- : exclusive upper bound (increasing)
a..<b - : inclusive upper bound (increasing) — replaces deprecated
a..<=ba..=b - : exclusive lower bound (decreasing)
a>..b - : inclusive lower bound (decreasing)
a>=..b
mbt
for i in 0..<10 { ... }
for i in 10>=..0 { ... }- :上界排他(递增范围)
a..<b - :上界包含(递增范围)——替代已废弃的
a..<=ba..=b - :下界排他(递减范围)
a>..b - :下界包含(递减范围)
a>=..b
mbt
for i in 0..<10 { ... }
for i in 10>=..0 { ... }3.9 Loop nobreak
nobreak3.9 循环nobreak
nobreakReplaces old loop . Executes when the loop condition becomes false.
elsembt
let r = while i > 0 {
if cond { break 42 }
i = i - 1
} nobreak {
7
}breaknobreak替代旧版循环的。当循环条件变为false时执行。
elsembt
let r = while i > 0 {
if cond { break 42 }
i = i - 1
} nobreak {
7
}breaknobreak3.10 declare
Keyword
declare3.10 declare
关键字
declareSimilar to Rust's macro. Use before function definitions to indicate specification-only declarations. Missing implementations generate warnings (not errors).
todo!declarembt
declare fn add(x : Int, y : Int) -> Int类似Rust的宏。在函数定义前使用表示仅为规范声明。缺失的实现会生成警告(而非错误)。
todo!declarembt
declare fn add(x : Int, y : Int) -> Int4. Performance Optimization
4. 性能优化
-
Lazy evaluation with Iterator: For array processing where the size is unknown or potentially large, preferfor lazy evaluation to avoid intermediate array allocations. This is especially effective for fold operations like
iter()/minimum():maximum()mbtlet min_x = coords.iter().map(c => c.x()).minimum().unwrap() -
Flattening: Useinstead of manual loops with
flatten()when merging nested collections.append
-
使用Iterator延迟求值:对于大小未知或可能很大的数组处理,优先使用进行延迟求值,避免中间数组分配。这在
iter()/minimum()这类折叠操作中效果尤为显著:maximum()mbtlet min_x = coords.iter().map(c => c.x()).minimum().unwrap() -
扁平化处理:合并嵌套集合时,使用而非手动循环加
flatten()。append
5. Toolchain
5. 工具链
- DSL replaces deprecated
moon.pkg.moon.pkg.json
- DSL替代已废弃的
moon.pkg。moon.pkg.json
6. Testing
6. 测试
See MoonBit Testing Standards for detailed testing guidelines.
详细测试指南请参考MoonBit测试标准。