大家好,我是 MoonBit 使用者,也是 MoonBit 布道师,我的个人网站是
https://chensuiyi.me
。
这是我的《改变世界的编程语言MoonBit》
系列文章,将自己学习和理解 MoonBit 的过程分享给大家,希望能带来参考和帮助。
全部文章可以前往 MoonBit 开发网https://moonbit.edgeone.app
或https://moonbit.pages.dev
查看,我坚信,MoonBit 将会改变世界。
上一篇文章 👉 :改变世界的编程语言 MoonBit:背景知识速览
很多编程语言入门教程,一开始都会教你这个编程语言的语法细节,我觉得这会让我们过早地进入细节而不知全貌。
正所谓一叶障目,不见泰山。如果一开始就林中行走,很难对这门语言产生一个更直观,更整体的印象,我们要登高望远,要横看成岭侧成峰,要远近高低各不同。
所以本文呢,来讲一讲,MoonBit的目录架构
。
语言文件
MoonBit 语言的专属语言扩展名是 .mbt
,可能有人会疑惑为什么不是 .mb
?这里我没有找到官方的相关说明,根据我的猜测,.mb
这种二字目扩展名基本已经被很多软件或项目使用了,所以把 MoonBiT
中的 3 个大写字母提取出来作为扩展名,可以有效地防止扩展名歧义和冲突。
测试文件
测试
是保证一个项目,文件,功能,代码的正确性,可用性,维护性的重要环节,在 MoonBit 中,专门为 测试
系统进行了 人性化
的设计。
mbt
test "test_name" {
assert_eq(1 + 1, 2)
assert_eq(2 + 2, 4)
inspect([1, 2, 3], content="[1, 2, 3]")
}
除了可以在 .mbt
文件中写 测试块
之外,还提供了 黑盒测试
与 白盒测试
。
只能访问包中公开成员的测试称为 黑盒测试
,能够访问包中所有成员的测试称为 白盒测试
,二者都提供了单独的文件来编写和管理测试用例。
黑盒测试的文件扩展名以 _test.mbt
结尾,白盒测试的文件扩展名以 _wbtest.mbt
结尾。
包管理
不管你是前端还是后端,是 JavaScript、Java、Go、Rust 还是 PHP 或 Python,一个现代化的语言,包管理
功能是基础的,也是必备的。
以下是编程语言的包管理机制:
- JavaScript :npm / yarn / pnpm →
package.json
- Java :Maven / Gradle →
pom.xml
- Go :Go Modules →
go.mod
- Rust :Cargo →
Cargo.toml
- Python :pip / pipenv / poetry →
requirements.txt
- PHP :Composer →
composer.json
所以 MoonBit 的包管理是什么呢?那就是 moon
→ moon.mod.json
和 moon.pkg.json
。

与 JavaScript 的 npm 一样,moon 不仅承载了包管理的功能,还包括了发布,安装,管理,编译,运行,查看等一系列功能。
模块
一个 项目
对应一个 模块
,一个 模块
对应一个 moon.mod.json
文件。
moon.mod.json
文件位于项目的根目录,可以看成是现代前端开发中的 package.json
文件。
包
一个模块是由 1 个或多个 包
组成的,每个 包
用 moon.pkg.json
文件进行标识和定义。
项目结构
mbt
my_project
├── Agents.md
├── cmd
│ └── main
│ ├── main.mbt
│ └── moon.pkg.json
├── LICENSE
├── moon.mod.json
├── moon.pkg.json
├── my_project_test.mbt
├── my_project.mbt
├── README.mbt.md
└── README.md -> README.mbt.md
MoonBit 的项目组织方式与其他语言稍有不同,如上,是通过 moon new
创建的一个新项目,常见的 LICENSE
和 README.md
自不必多说,分别是 许可协议
和 项目说明书
。
而 README.mbt.md
则是 MoonBit 独有的文件,可以理解为 可编程的说明书
,也就是说在 README.mbt.md
文件中写的 MoonBit 代码,与在 .mbt
文件中写代码是没有区别的,照样可以做到编译器的语法校验,提示,修复等。
那么 my_project.mbt
和 my_project_test.mbt
呢,则是根据项目名称 my_project
默认创建的 同名
语言文件。
模块配置文件 moon.mod.json
的内容如下,chensiuyi
是我们的用户名,my_project
是项目 (模块
) 名,用这 2 个名称,可以唯一标识一个项目或模块。
json
{
"name": "chensuiyi/my_project",
"version": "0.1.0",
"readme": "README.md",
"repository": "",
"license": "Apache-2.0",
"keywords": [],
"description": ""
}
所以,我们在说到 包
的时候,一般是要带上 模块前缀
的,比如根目录下的 moon.pkg.json
则是 chensuiyi/my_project
包。
而 cmd/main
目录下的 moon.pkg.json
,则是 chensuiyi/my_project/cmd/main
包。
所以以上项目一共有 2 个 包
,分别是 chensuiyi/my_project
包和 chensuiyi/my_project/cmd/main
包。
chensuiyi/my_project/cmd/main
包是我们的主包,其 moon.pkg.json
文件内容如下:
json
{
"is-main": true,
"import": [
{
"path": "chensuiyi/my_project",
"alias": "lib"
}
]
}
主包用 is-main
字段为 true
来标记,表示这个包是 moon run
指令的入口,同时也是 moon build
的入口,一个项目可以有 0个
或 多个
主包。
import
则是包与包之间的互相依赖,如上配置,主包 chensuiyi/my_project/cmd/main
依赖了 chensuiyi/my_project
包,并为其设置别名为 lib
。
凡是有 moon.pkg.json
文件的目录都是 包
,而根目录下的 moon.pkg.json
文件则是一个空文件,默认内容只有一个大括号 {}
(更多配置后文详解),表示是一个普通包。
项目架构的哲学
mbt
my_project
├── Agents.md
├── cmd
│ └── main1
│ ├── main.mbt
│ └── moon.pkg.json
│ └── main2
│ ├── main.mbt
│ └── moon.pkg.json
├── math
│ ├── plus.mbt
│ └── moon.pkg.json
├── LICENSE
├── moon.mod.json
├── moon.pkg.json
├── my_project_test.mbt
├── my_project.mbt
├── README.mbt.md
└── README.md -> README.mbt.md
按照我的个人的理解来说,本文的 项目结构
部分还不足以说明 MoonBit 的架构哲学,所以我把 项目结构
进行了如上扩展。
那么先问个问题,以上有几个 包
?答案是 4个
,很简单,有几个 moon.pkg.json
就有几个 包
,就是如此简单。
根目录下的 moon.pkg.json
跟 cmd
和 math
目录下的 moon.pkg.json
没有区别,就像是一个班级的学生,大家都是平等的 包
。
is-main
配置为 true
或 false
则表明了这个学生的是否有职务,为 true
则在普通包的基础上,有了 主包
、入口
的 职务
。


所以当我们执行 moon build --target all
命令的时候,就会生成上图中的 js
、native(exe)
,wasm
和 wasm-gc
4 种编译结果 (llvm目前还不稳定,只在nightly版本下生成
),每个编译结果都有 main1
和 main2
两个文件,与我们上面的配置一致。
如果你写过 go 语言,会发现在 go 约定成俗的规范中,cmd 目录就是应用程序的主入口目录,也是用于生成二进制的目录,所以写过 go 的小伙伴对这个 哲学
应该不会陌生。
内部包
mbt
my_project
├── Agents.md
├── cmd
│ └── main1
│ └── internal
│ └── math
│ ├── minus.mbt
│ └── moon.pkg.json
│ ├── main.mbt
│ └── moon.pkg.json
│ └── main2
│ ├── main.mbt
│ └── moon.pkg.json
├── math
│ ├── plus.mbt
│ └── moon.pkg.json
├── LICENSE
├── moon.mod.json
├── moon.pkg.json
├── my_project_test.mbt
├── my_project.mbt
├── README.mbt.md
└── README.md -> README.mbt.md
如果你想创建一个包,但是只能被 main1
包使用,那可以在 main1
目录下创建一个 internal
目录,那么这个目录下的包就只能被 main1
及其子目录使用。
虚拟包
虚拟包是一个实验性功能,可能存在错误和未定义的行为。
json
{
"virtual": {
"has-default": true
}
}
在 moon.pkg.json
文件中,有定义 virtual
字段的,就是 虚拟包
。
顾名思义,虚拟包就是对 实现的定义
,而不是 具体的实现
,它们可以在构建时被特定的实现替换。
目前,虚拟包只能包含普通函数。虚拟包在保持代码不变的情况下,可以在不同的实现之间进行切换。
发布包

MoonBit 的中文名称叫做 月兔
,每个模块称之为 月饼
,放月饼的仓库就是 https://mooncakes.io
。
所有人都可以发布自己的 月饼
到仓库中,被其他人依赖并使用。
结尾
这就是 MoonBit 目录,文件
的架构 哲学
,带大家飞到天上总览全局,有一个大概的轮廓和印象。
下一篇 改变世界的编程语言MoonBit:搞懂配置文件
,我们不见不散。