属性#

属性是放置在源码中的结构之前的信息。它们都采用 #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 属性能够在如下上下文中使用:

  • 顶层的值声明(包括 fnletconst

  • 顶层的类型声明(包括 typestructenum

  • 特征声明

  • 特征的默认实现

它有三种形式:

  • #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 属性#

备注

这里的文档不涉及访问控制。关于如何使用pubpub(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_tomessage

  • change_to参数是一个表示该类型新的可见性的字符串,它可以是"abstract"或者"readonly"

    "change_to"的值

    将要禁止的使用

    "readonly"

    创建该类型的实例,或者变更实例的字段的值。

    "abstract"

    创建该类型的实例、变更实例的字段的值、对它的实例进行模式匹配或者访问实例的字段。

  • message参数用于提供额外的信息,告诉使用者关于可见性的变更。

internal 属性#

#internal属性用于标记一个函数、类型或者特征为内部的。所有在模块外的使用将会触发警告。

#internal(unsafe, "这是个不安全的函数")
fn unsafe_get[A](arr : Array[A]) -> A {
  ...
}

“internal”属性接受两个参数:categorymessagecategory 是一个标识符,表示警示对应的类别。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

borrowowned 属性#

#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"))