MoonBit 语言导览 MoonBit

特征

假如说方法定义了类型的行为,那么特征(trait)则总结了一组类型所共有的行为。在定义一个全新的 特征时,通常需要编写两个部分:

  • 特征定义: 使用trait关键字定义,它描述了特征所包含的方法签名。
  • 特征实现: 使用impl关键字为特定类型定义特征的实现。

在示例中,我们定义了一个Equal特征,它总结了一个is_equal方法:接受两个Self类型的参数, Self类型表示实现该特征的具体类型;方法返回一个布尔值,在这里用来表示Self类型的两个值 是否相等。

我们分别为内建的Int类型和自己定义的Pos类型实现了Equal特征,提供了is_equal方法的具体实现。 因为特征定义已经声明过is_equal方法的类型,所以在实现中可以省略参数和返回值的类型标注。

main函数中,通过Equal::is_equal()可以调用该特征总结的方法,根据传入的ab类型,不同类型 会调用各自的实现版本。 也就是说:

  • abInt类型时,执行的是impl Int for Equal with is_equal内的代码。
  • abPos类型时,执行的是impl Pos for Equal with is_equal内的代码。

调用哪个实现版本是静态地决定的,没有运行时动态分派的开销。

限制

在为类型实现特征时,MoonBit 遵循孤儿规则(orphan rule):impl Type for Trait ...实现必须和TypeTrait在同一个包, 而不允许孤立地存在于某个包中。 这一限制保证了特征实现的唯一性,避免了不同包中对同一类型和特征的重复实现引发冲突,或者因为其他包的变化而改变已有代码的行为。

用途

其实,在MoonBit的标准库的moonbitlang/core/builtin包中就定义了这样一个特征,名为Eq, 它总结了一个equal方法,用于比较两个值是否相等。并且标准库已经为许多内建类型实现了Eq特征, 比如IntBoolArray等。标准库还提供了Show特征,用于将值转换为字符串表示。 我们后续将会介绍这些特征的使用场景。

你可能会疑惑为什么要使用trait, 而不是直接分别为类型定义is_equal方法。 下一节我们将介绍特征的一个重要用途:特征约束

///|
trait Equal {
  is_equal(Self, Self) -> Bool
}

///|
impl Equal for Int with is_equal(a, b) {
  a == b
}

///|
struct Pos(Int, Int)

///|
impl Equal for Pos with is_equal(a, b) {
  let Pos(x1, y1) = a
  let Pos(x2, y2) = b
  x1 == x2 && y1 == y2
}

///|
fn main {
  println(Equal::is_equal(1, 1))
  println(Equal::is_equal(1, 2))
  let pos1 = Pos(1, 2)
  let pos2 = Pos(1, 2)
  let pos3 = Pos(4, 5)
  println(Equal::is_equal(pos1, pos2))
  println(Equal::is_equal(pos2, pos3))
}