属性#
Attributes are annotations placed before structures in source code. They take the form #attribute(...).
An attribute occupies the entire line, and newlines are not allowed within it.
Attributes do not normally affect the meaning of programs. Unused attributes will be reported as warnings.
属性的语法如下所示:
attribute ::= '#' attribute-name
| '#' attribute-name '(' attribute-arguments ')'
attribute-name ::= LIDENT | LIDENT '.' LIDENT
attribute-arguments ::= attribute-argument (',' attribute-argument )*
attribute-argument ::= expr | LIDENT '=' expr
expr ::= LIDENT | UIDENT | STRING | 'true' | 'false'
| LIDENT '.' LIDENT
| LIDENT '(' attribute-arguments ')'
| LIDENT '.' LIDENT '(' attribute-arguments ')'
属性有两种类别:内建属性和用户自定义属性。例如:
#deprecated("message")
#custom.attribute(key="value", flag=true)
第一个属性是一个内建的属性,在属性名中,它没有命名空间前缀。内建属性会被 MoonBit 编译器检查,并且有特殊的含义。
第二个属性是用户自定义属性,它的名字中包含命名空间前缀。用户自定义属性会被编译器忽略,通过解析源码,它能够被外部的工具使用。
备注
MoonBit 在设计上有意不支持运行时反射。运行时反射容易被滥用,使得工具链(例如编译器)在编译时不可能捕获到错误,让代码的维护变得更困难。它也会对性能优化产生负面影响。
我们更倾向于使用编译时的代码生成,维持静态类型分析和性能上的优势(代码生成也需要节约地使用,避免不必要的复杂性)。
deprecated 属性#
The #deprecated attribute is used to mark an API as deprecated. MoonBit emits
a warning when the deprecated API is used, and if the API is listed in completion,
it will be shown with a strikethrough style. For example:
#deprecated
pub fn foo() -> Unit {
...
}
#deprecated("Use Bar2 instead")
pub(all) enum Bar {
Ctor1
Ctor2
}
#deprecated 属性能够在如下上下文中使用:
顶层的值声明(包括
fn,let和const)顶层的类型声明(包括
type,struct和enum)特征声明
特征的默认实现
常见形式包括:
#deprecated将这个项目标记成弃用的,并使用默认的警告信息。
#deprecated("使用 new_function 代替")将 API 标记为弃用,并自定义一个警告信息。每当这个弃用的 API 被使用时,自定义信息将会显示在警告中。
#deprecated("使用 new_function 代替", skip_current_package=true)将 API 标记为弃用的同时自定义警告信息,但是当 API 在同一个包内使用时不触发警告。
#deprecated(skip_current_package=true)Marks the item as deprecated with a default warning message, but skips emitting warnings within the same package. When both a message and
skip_current_packageare present, either argument order is accepted.
Alert Attribute#
#alert 属性会为 API 附加一个类别和一条消息。当代码使用该 API 时,MoonBit 会发出 alert 警告。
#alert(unsafe, "This function is unsafe.")
fn[A] alert_unsafe_get(arr : Array[A], index : Int) -> A {
arr[index]
}
第一个参数是 alert 类别,第二个参数是展示给用户的消息。该警告可以通过 alert、alert_unsafe 等警告名称配置。
For more detail, see alert warning.
alias 属性#
alias 属性用于重载一个和索引操作相关的运算符,或者为一个顶层函数与变量创建别名。它有两种形式:
#alias("op"):op是下面其中一个字符串,表示对应的索引操作:_[_]:数组索引操作符_[_]=_:数组索引赋值操作符_[_:_]:op_as_view 操作符
#alias(id): 其中id是一个代表别名的标识符
所有形式都支持额外的参数:
visibility="modifier"一个带标签的参数,用于改变别名的可见性。
modifier可以是pub或者priv,如果没有声明,别名会和原始的函数或者变量有一样的可见性。deprecated或者deprecated="message"将别名标记为弃用的。如果提供了
message信息,当别名被使用时,信息会被一起显示在警告中。
To graceful migration from old API to new API, you can rename the old API directly, and create an alias with the old name, mark it as deprecated. For example:
#alias(old_name, deprecated)
fn new_name() -> Unit {
()
}
label_migration 属性#
The #label_migration attribute is used to help you safely evolve your API
by warning users during the transition period.
它有如下三种形式:
#label_migration(id, fill=true, msg="message")The
fillargument is used when you want to refactor an optional parameter. You can usefill=truewhen you want to eventually make an optional parameter required. You can usefill=falsewhen you want to eventually remove an optional parameter.msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, fill=true) #label_migration(y, fill=false) fn label_migration_fill(x? : Int = 0, y? : Int = 1) -> Int { x + y }
#label_migration(id, allow_positional=true, msg="message")The
allow_positionalargument is used when you want a labelled parameter to be used without its label being provided. When the parameter is used positionally (without a label), the compiler reports a warning. This is useful when you want to change a positional parameter to a labelled parameter without breaking the downstream code.msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, allow_positional=true) fn label_migration_allow_positional(x~ : Int) -> Int { x }
#label_migration(id, alias=new_id, msg="message")The alias argument allows you to provide an alternative name to a labelled parameter. This is useful when renaming a parameter to maintain backward compatibility. If a warning message is provided, the compiler warns when using the alias; otherwise, the alias can be used without warnings.
msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, alias=xx) #label_migration(x, alias=y, msg="warning") fn label_migration_alias(x~ : Int) -> Int { x }
visibility 属性#
备注
这里的文档不涉及访问控制。关于如何使用 pub、pub(all) 和 priv,见访问控制。
The #visibility attribute is similar to the #deprecated attribute, but it is used to hint that a type will change its visibility in the future.
For outside usages, if the usage will be invalidated by the visibility change in future, a warning will be emitted.
// in @util package
#visibility(change_to="readonly", "Point will be readonly in the future.")
pub(all) struct Point {
x : Int
y : Int
}
#visibility(change_to="abstract", "Use new_text and new_binary instead.")
pub(all) enum Resource {
Text(String)
Binary(Bytes)
}
pub fn new_text(str : String) -> Resource {
...
}
pub fn new_binary(bytes : Bytes) -> Resource {
...
}
// in another package
fn main {
let p = Point::{ x: 1, y: 2 } // warning
let { x, y } = p // ok
println(p.x) // ok
match Resource::Text("") { // warning
Text(s) => ... // waning
Binary(b) => ... // warning
}
}
The #visibility attribute takes a required change_to argument and an
optional message argument.
change_to参数是一个表示该类型新的可见性的字符串,它可以是"abstract"或者"readonly""change_to"的值将要禁止的使用
"readonly"创建该类型的实例,或者变更实例的字段的值。
"abstract"创建该类型的实例、变更实例的字段的值、对它的实例进行模式匹配或者访问实例的字段。
The optional
messageargument is a string that provides additional information about the visibility change.
internal 属性#
The #internal attribute is used to mark a function, type, or trait as internal.
Any usage of the internal function or type in other modules will emit an alert warning.
#internal(unsafe, "This is an unsafe function")
fn[A] internal_unsafe_get(arr : Array[A], index : Int) -> A {
arr[index]
}
The internal attribute takes a required category argument and an optional
message argument. category is a identifier that indicates the category of
the alert, and message is a string that provides additional message for the
alert.
The alert warnings can be turn off by setting the warn-list in moon.pkg.
For more detail, see alert warning.
warnings 属性#
The #warnings attribute is used to configure warning settings for a specific
top-level declaration. It can enable, disable or treat an enabled warning as error
for specific warnings in that declaration.
The argument is a string that specifies the warning list. It can contain multiple warning names, each prefixed with a sign:
#warnings("-unused_value")
fn warnings_example() -> Unit {
let x = 42
}
这些前缀有以下含义:
+warning_name: 启用这个警告-warning_name: 关闭这个警告@warning_name: 如果这个警告已经启用,转换为错误
目前这个属性只对部分特定的警告工作。
To learn more about warning names, see warning list.
Must Implement One 属性#
#must_implement_one 属性用于 trait,要求每个实现都显式定义至少一个方法,而不是只依赖默认方法实现。
不带参数时,必须显式实现该 trait 的至少一个方法:
#must_implement_one
pub(open) trait RequireAnyMethod {
f(Self) -> Unit = _
g(Self) -> Unit = _
}
impl RequireAnyMethod with f(_) {}
impl RequireAnyMethod with g(_) {}
type AnyImpl
impl RequireAnyMethod for AnyImpl with f(_) {}
带方法名时,必须显式实现列出的方法中的至少一个:
#must_implement_one(f, g)
pub(open) trait RequireSelectedMethod {
f(Self) -> Unit = _
g(Self) -> Unit = _
h(Self) -> Unit = _
}
impl RequireSelectedMethod with f(_) {}
impl RequireSelectedMethod with g(_) {}
impl RequireSelectedMethod with h(_) {}
type SelectedImpl
impl RequireSelectedMethod for SelectedImpl with g(_) {}
同一个 trait 上可以使用多个 #must_implement_one 属性,以要求显式实现多个方法组中的方法。
Inline Attribute#
#inline 属性是函数的优化提示。它会请求编译器尽可能内联该函数:
#inline
fn add_one(x : Int) -> Int {
x + 1
}
使用 #inline(never) 可以请求编译器不要内联某个函数:
#inline(never)
fn keep_stack_frame(x : Int) -> Int {
x + 1
}
这些属性只是提示,不会改变函数在源码层面的行为。
external 属性#
#external属性用于标记一个抽象类型为外部类型。
对于 Wasm 和 Wasm GC 后端,它将被解释为
externref。对于 JavaScript 后端,它将被解释为
any。对于原生后端,它将被解释为
void*。
#external
type AttrPtr
borrow 和 owned 属性#
#borrow 和 #owned 属性用于 FFI 声明,用来描述带引用计数的 MoonBit 参数如何传递给外部代码。这对于 Bytes、String、FixedArray[T] 以及抽象类型等装箱的 MoonBit 值很重要;这些值在 C 和 Wasm 后端上的生命周期由引用计数管理。
当外部函数只在调用期间读取 param,且不会存储或返回它时,使用 #borrow(param)。借用参数仍由 MoonBit 拥有,因此外部函数不需要为它调用 moonbit_decref 或 $moonbit.decref。
当外部函数接管 param 的所有权时,例如将它存储起来并在之后释放,使用 #owned(param)。owned 参数最终必须由外部侧在不再需要时释放。
#borrow(filename)
extern "C" fn open(filename : Bytes, flags : Int) -> Int = "open"
完整的调用约定规则见 FFI 生命周期管理。
as_free_fn 属性#
#as_free_fn 属性用于标记一个方法,同时它也被声明为一个自由函数。它还可以改变自由函数的可见性、自由函数的名字,并且提供单独的弃用警告。
#as_free_fn(dec, visibility="pub", deprecated="use `Int::decrement` instead")
#as_free_fn(visibility="pub")
fn Int::decrement(i : Self) -> Self {
i - 1
}
test {
let _ = decrement(10)
let _ = (10).decrement()
}
callsite 属性#
#callsite 属性用于标记发生在调用位置的属性。
它可以是 autofill,用于在调用位置自动填充参数 SourceLoc 和 ArgLoc。
skip 属性#
The #skip attribute is used to skip a single test block. It can be written as
#skip or with a reason, such as #skip("blocked by external service"). The
type checking will still be performed.
Coverage Skip Attribute#
#coverage.skip 属性会跳过函数内的覆盖率操作。
#coverage.skip
fn platform_specific_helper() -> Unit {
()
}
它适用于不应影响覆盖率报告的函数,例如特定平台的回退代码,或有意排除在覆盖率统计之外的代码路径。更多细节见跳过覆盖率。
cfg 属性#
#cfg 属性用于执行条件编译。例子有:
#cfg(true)
fn cfg_true() -> Unit {
()
}
#cfg(false)
fn cfg_false() -> Unit {
()
}
#cfg(target="wasm")
fn cfg_wasm() -> Unit {
()
}
#cfg(not(target="wasm"))
fn cfg_not_wasm() -> Unit {
()
}
#cfg(all(target="wasm", true))
fn cfg_all() -> Unit {
()
}
#cfg(any(target="wasm", target="native"))
fn cfg_any() -> Unit {
()
}
模块属性#
module 属性用于为 JavaScript 后端声明模块依赖。
在 cjs 格式中,它被解释为 require,而在 esm 格式中,它被解释为 import。
#module("math-utils")
pub extern "js" fn add_from_module(x : Int, y : Int) -> Int = "add"