属性#
属性是放置在源码中的结构之前的信息。它们都采用 #attribute(...) 的形式。一个属性占用整行的空间,其中不允许换行。属性一般不会影响程序的含义。无用的属性将会触发警告。
属性的语法如下所示:
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 属性#
#deprecated属性用于标记一个 API 为弃用状态。当弃用的 API 在其他包中被使用时,MoonBit 将会触发警告,而且如果 API 出现补全列表中,它会被加上删除线的样式。例如:
#deprecated
pub fn foo() -> Unit {
...
}
#deprecated("使用Bar2代替")
pub 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 在同一个包内使用时不触发警告。
alias 属性#
alias 属性用于重载一个和索引操作相关的运算符,或者为一个顶层函数与变量创建别名。它有两种形式:
#alias("op"):op是下面其中一个字符串,表示对应的索引操作:_[_]:数组索引操作符_[_]=_:数组索引赋值操作符_[_:_]:op_as_view 操作符
#alias(id): 其中id是一个代表别名的标识符
所有形式都支持额外的参数:
visibility="modifier"一个带标签的参数,用于改变别名的可见性。
modifier可以是pub或者priv,如果没有声明,别名会和原始的函数或者变量有一样的可见性。deprecated或者deprecated="message"将别名标记为弃用的。如果提供了
message信息,当别名被使用时,信息会被一起显示在警告中。
如果要优雅地将一个旧的 API 迁移到新的 API,可以直接将旧的 API 重命名成新的名字;然后,使用旧名字创建一个别名,同时将它标记成弃用的。
#alias("old_name", deprecated)
fn new_name() -> Unit {
...
}
label_migration 属性#
#label_migration 属性用于帮助你安全地演进 API,在迁移期间给下游的用户警告。
它有如下三种形式:
#label_migration(id, fill=true, msg="message")当你想重构一个可选参数时,可以设置
fill。当fill=true时,这代表最终这个参数将变成必选的;当fill=false时,这代表最终这个参数会被移除。msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, fill=true) #label_migration(y, fill=false) fn f(x?: Int = 0, y?: Int = 1) -> Unit { ... } fn main { f(x=1, y=1) // warn on y being filled f() // warn on x not being filled }
#label_migration(id, allow_positional=true, msg="message")当你希望带标签的参数可以在不提供标签的情况下使用时,可以设置
allow_positional。当参数按位置使用(没有标签)时,编译器会报告警告。这在你想要将位置参数更改为带标签参数,并且不破坏下游代码时非常有用。msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, allow_positional=true) fn f(x~: Int) -> Unit { ... } fn main { f(42) // warn on positional argument 42 used without label }
#label_migration(id, alias=new_id, msg="message")alias属性允许为带标签的参数提供替代名称。这在重命名参数以保持向后兼容性时很有用。如果提供了警告消息,编译器在使用别名时会发出警告;否则,可以在不发出警告的情况下使用别名。msg参数是一个字符串,用于提供额外的信息,告诉使用者关于可见性的变更。#label_migration(x, alias=xx) #label_migration(x, alias=y, msg="warning") fn f(x~: Int) -> Unit { ... } fn main { f(xx=42) // no warning f(y=42) // warning }
visibility 属性#
备注
这里的文档不涉及访问控制。关于如何使用pub、pub(all) 和 priv, 见 访问控制.
#visibility属性和#deprecated属性类似。但它用于提示用户一个类型未来将改变它的可见性。对于外部的包,如果使用这个类型的方式在未来改变可见性以后不再合法,它将会触发警告。
// 在包 @util 中
#visibility(change_to="readonly", "Point将会变成只读的类型")
pub(all) struct Point {
x : Int
y : Int
}
#visibility(change_to="abstract", "使用 new_text 和 new_binary 代替")
pub(all) enum Resource {
Text(String)
Binary(Bytes)
}
pub fn new_text(str : String) -> Resource {
...
}
pub fn new_binary(bytes : Bytes) -> Resource {
...
}
// 在另一个包中
fn main {
let p = Point::{ x: 1, y: 2 } // 触发警告
let { x, y } = p // ok
println(p.x) // ok
match Resource::Text("") { // 触发警告
Text(s) => ... // 触发警告
Binary(b) => ... // 触发警告
}
}
#visibility属性接受两个参数:change_to 和 message。
change_to参数是一个表示该类型新的可见性的字符串,它可以是"abstract"或者"readonly""change_to"的值将要禁止的使用
"readonly"创建该类型的实例,或者变更实例的字段的值。
"abstract"创建该类型的实例、变更实例的字段的值、对它的实例进行模式匹配或者访问实例的字段。
message参数用于提供额外的信息,告诉使用者关于可见性的变更。
internal 属性#
#internal属性用于标记一个函数、类型或者特征为内部的。所有在模块外的使用将会触发警告。
#internal(unsafe, "这是个不安全的函数")
fn unsafe_get[A](arr : Array[A]) -> A {
...
}
“internal”属性接受两个参数:category 和 message。category 是一个标识符,表示警示对应的类别。message 是一个字符串,表示警示中额外的提示信息。
alert警告可以通过配置moon.pkg.json中的warn-list选项来关闭。关于更多的细节,见 alert 警告.
warnings 属性#
#warnings属性用于配置单个顶层的声明的警告。在声明内,它能够启用、关闭警告,或者将一个已经启用的警告配置为错误。
它的参数是一个声明了警告列表的字符串。字符串包含多个警告名,每个名字以符号开头:
#warning("-unused_value@deprecated")
fn f() -> Unit {
let x = 42
}
这些前缀有以下含义:
+warning_name: 启用这个警告-warning_name: 关闭这个警告@warning_name: 如果这个警告已经启用,转换为错误
目前这个属性只对部分特定的警告工作。
要了解更多关于警告名的信息,见 警告列表
external 属性#
#external属性用于标记一个抽象类型为外部类型。
对于 Wasm(GC) 后端,它将被解释为
anyref。对于 JavaScript 后端,它将被解释为
any。对于原生后端,它将被解释为
void*。
#external
type Ptr
borrow 和 owned 属性#
#borrow 和 #owned 属性用于表示一个外部接口是否获得参数的所有权。对于更多细节,见 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 属性#
#skip 属性用于跳过一个测试块。类型检查仍然会被执行。
cfg 属性#
#cfg 属性用于执行条件编译。例子有:
#cfg(true)
#cfg(false)
#cfg(target="wasm")
#cfg(not(target="wasm"))
#cfg(all(target="wasm", true))
#cfg(any(target="wasm", target="native"))