首页/MoonBit/为什么MoonBit的println和Json可以直接用?

刚开始写 MoonBit 时,有一个疑问:为什么下面的代码可以直接使用 println 和 Json?

println("Hello, MoonBit!")

let data : Json = { "name": "MoonBit" }

经过我一番研究,发现 println 和 Json 的基础定义来自 MoonBit 标准库里的 moonbitlang/core/builtin。

它是最基础的一层标准库包,提供基础类型和常用基础能力。

moonbitlang/core/prelude 是一个普通包。

MoonBit 默认让它在普通程序中可用。

它会导出 builtin 里的常用名称,所以普通代码可以直接使用它们。

后文的 builtin 和 prelude,说的就是这两个包。

可以把这条链路理解成:

builtin 提供基础能力
    ↓
prelude 用 pub using 导出常用名称
    ↓
普通 MoonBit 文件默认可以直接使用这些名称

prelude 为什么特殊?

prelude 是一个标准库包,不是 MoonBit 语法。

它的特殊之处在于:编译器会将它带入普通源文件的可见范围。

因此,println 的定义在 builtin 中,但 prelude 导出了这个名字。

Json 也一样。

builtin 提供该类型,prelude 再将它导出。

写普通代码时,不需要为这两个名称显式写 using。

这里的关键是 “自动可见” 这条编译器规则,而不是 prelude 本身变成了语法。

其他标准库名称能否直接使用,要看它是否由 prelude 导出。

还有一类 “直接可用”:语言和内建能力

并非所有不需要导入的内容都来自 prelude。

还有一些内容属于 MoonBit 语言本身,或属于与编译器和运行时紧密配合的基础内建能力。

let value : Int? = Some(42)
let result : Result[Int, String] = Ok(42)

match result {
  Ok(number) => println(number)
  Err(message) => println(message)
}

其中:

  • let、fn、match、if、for 等是语言语法。
  • Int、String、Bool、Unit、Option、Result 等是基础内建类型。
  • Some、None、Ok、Err 分别是 Option 和 Result 的构造器。
  • ? 是 Option 的类型缩写。

例如 Int? 表示一个可能存在、也可能不存在的 Int。

可以这样记:

语言负责让 MoonBit 理解代码的写法和基础数据。

builtin 提供基础能力。

prelude 把最常用的名称带到普通代码的可见范围中。

当前不需要导入即可使用的 prelude 名称

下面是当前 prelude 公开且未废弃的名称。

它们可以直接写在普通 MoonBit 代码中。

函数和常量

  • abort、assert_true、assert_false、compare、debug_assert
  • fail、ignore、hash、inspect、panic、physical_equal、println
  • debug、debug_inspect、repr、json_inspect
  • assert_eq、assert_not_eq
  • null

类型和关联构造器

  • Json(...):把值转换成 Json 的关联构造器,随 Json 类型一起可用。
  • Buffer、Set、BigInt
  • ArgsLoc、Array、ArrayView、MutArrayView、UninitializedArray
  • Failure、Hasher、Iter、Iter2
  • Json、Map、InspectError、SnapshotError、SourceLoc、StringBuilder
  • Repr、Regex、Ref、Lazy

可在类型声明中直接使用的接口名

  • Eq、Compare、Hash、Logger、Show、ToJson、Default
  • Add、Sub、Mul、Div、Mod、Neg、Shl、Shr、BitAnd、BitOr、BitXOr
  • Debug、FromJson

这份名单以当前 moonbitlang/core 仓库的 prelude/prelude.mbt 为准。

标准库仍在演进,后续版本可能调整默认导出的范围。

什么时候仍然需要显式引入?

当需要的名称不在 prelude 中时,就按对应包的使用方式引入。

例如,某个集合、随机数或命令行相关能力没有出现在上面的默认名称清单中,就不能因为它属于标准库而直接使用。

prelude 只导出一小组高频名称,不会覆盖所有标准库能力。

遇到 “为什么这个标准库名称不能直接用” 的问题时,先检查它是否由 prelude 导出,通常就能找到答案。