跳到主要内容

14 篇博文 含有标签「MoonBit」

查看所有标签

MoonBit 正式开源核心编译器

· 阅读需 6 分钟

cover

MoonBit(月兔)AI 原生开发平台今日宣布正式开源其核心编译器 ( WebAssembly 后端),可在 GitHubGitee 平台查看源码。

作为开放开源的开发平台。MoonBit支持多后端(WebAssembly,JavaScript,Native),全场景开发。

开源 Wasm 后端是 MoonBit 在 2024 年开源计划中的重要里程碑。这一后端是 MoonBit 当前最为成熟的技术模块,运行性能和代码生成体积相比于 Rust 和 Go 均有明显优势。作为工具链中的核心板块,Wasm 编译器在连接语言特性与实际应用方面也发挥着至关重要的作用。

今年以来,MoonBit 已先后开源了核心标准库构建系统,通过不断降低开发门槛和提升工具透明度,吸引了越来越多的开发者加入生态建设。

拥抱开源

WebAssembly(Wasm)作为一种高效、安全的跨平台技术,是 MoonBit 重点关注的领域之一。尽管 Wasm 的潜力巨大,但许多现有编程语言未能充分发挥其优势。为了弥补这一空白,MoonBit 创立之初就为 Wasm 做了大量的优化,力求在 Wasm 平台取得数量级的比较优势,并成功因此为吸引到了第一批来自包含欧美、日、韩等国的全球用户。

通过优化 Wasm 组件模型,MoonBit成功将一个简单的 「http-hello-world 」示例的代码体积压缩至 27kB,显著小于许多其他语言,进一步验证了 MoonBit-Wasm 的生产环境应用能力。过去的一年,MoonBit 在此基础上,继续拓展其应用场景成功支持了 JavaScript 和原生后端,未来有望在更多领域发挥作用。

开源 WebAssembly 编译器这一核心技术,未来开发者将有机会亲身体验到 MoonBit 在性能优化方面的卓越成果,进一步巩固了 MoonBit 生态的技术核心同时,也为开发者提供了直接参与技术优化和生态创新的机会。

为什么采用宽松版的 SSPL

MoonBit 将始终坚持以开放,开源为核心策略,而且 MoonBit 团队坚信,开发者需要的是透明、可靠的工具,而不是功能受限的“开源核心”(Open-Core)模式。这传递了一个重要信号:MoonBit 对于用户始终保持免费开放,无论何时全球开发者都能享受到最优质稳定可信赖的开发体验。

MoonBit 在 SSPL 的基础上放宽了两条限制

  1. MoonBit 编译器生成的成果可以由用户自行选择许可协议,用户拥有为自己的 MoonBit 源代码和生成成果自由选择许可的权利。

  2. 编译器的修改在非商业用途下(比如:学术研究)是被允许的。

MoonBit 选择了宽松版的 SSPL 许可协议而不是 MIT 或者 BSD,主要基于以下两点原因:

  1. 项目稳定性:MoonBit 目前仍处于测试预览阶段,此时引入硬分叉可能会影响项目的稳定性。团队希望在项目达到更成熟和稳定的状态后,再欢迎社区的广泛贡献。

  2. 商业保护:避免大型云服务商利用团队的成果进行商业化,而这些行为可能会削弱团队的努力。

当前,MoonBit 的战略聚焦在用户增长与生态构建。通过培育繁荣的生态系统,团队希望为海量用户带来由 AI 驱动的端到端开发体验,并通过高附加值的云端托管服务释放更大的价值。未来,MoonBit 也将大胆探索软硬件一体化的可能性,以更深度的整合方式打造商业闭环,充分释放技术潜力。

了解更多

MoonBit 支持 Native 后端

· 阅读需 13 分钟

cover.jpg

MoonBit 诞生于2022年,作为一款专为云计算与边缘计算设计的 AI 云原生编程语言及其完整工具链,融合了 Rust 和 Go 等现代语言的优秀设计理念。在 2023 年 8 月 18 日的首次公开展示中,MoonBit 支持了 WebAssembly 这一跨平台标准指令集,并实现了优越的运行速度和紧凑的代码体积。在 2023 年 5 月,MoonBit 支持 JavaScript 后端,并在性能上实现突破,在 JSON5 库的测试中,MoonBit JS 性能表现比原生JavaScript提升了近8倍。在已有 WebAssembly 和 JavaScript 后端的基础上,MoonBit 平台新增原生后端支持。这一原生后端能够直接将 MoonBit 代码编译为机器码在硬件上运行,摆脱了虚拟机的限制,显著提升了性能。

为什么要支持 Native 原生后端?

编程语言的运行时实现方式主要分为两类:基于虚拟机的(如 Java、Kotlin 等)和直接在硬件上执行的(如C/C++、Rust等)。原生后端虽然具备更大的优化空间和更高的性能上限,但开发难度也相应增加。

目前,MoonBit 已支持 WebAssembly 和 JavaScript 后端,凭借虚拟机运行具备良好的生态支持。然而,在高性能要求的场景中,这些后端可能存在局限。MoonBit 的原生后端能够将代码编译为目标机器的可执行二进制文件,避免解释开销,充分利用硬件资源,如 CPU、内存和存储,从而显著提升性能。对于视频处理、机器学习和大规模数据分析等计算密集型应用,原生后端提供了必要的性能支持。

通过实际的性能数据对比, 根据不同应用场景, MoonBit 原生后端在数值计算领域以及内容管理性能方面超速领先于传统编程语言 Java 以及 Swift。

数值计算领域:相比 Java 快一个数量级

原生语言相比于基于虚拟机的编程语言的性能优势在数值计算领域尤为明显,这得益于原生编程语言很大程度上能更好的优化内存布局和利用硬件加速指令。

在这个经典的FFT算法 benchmark 中,同样的 Cooley-Tukey 算法, MoonBit 相比 Java 有15倍以上的性能提升,比最先进的商业版本的 Java 编译器 GraalVM 也有4倍以上的性能提升。

下面是 MoonBit 与 Java 实现的 FFT 算法的性能对比。FFT(快速傅里叶变换)是一个计算序列的离散傅里叶变换的高效算法,在信号处理、压缩等许多领域有重要应用。FFT 算法涉及大量数值运算和数组操作,可以很好地体现语言的基础性能:

fft-benchmark.png

与传统的 C/C++ 等底层语言不同,MoonBit 在实现显著性能提升的同时,保留了极佳的开发体验。MoonBit 与 Java 一样,采用自动内存管理,避免了手动管理内存的复杂性。同时,MoonBit 通过更现代化的语言设计,提供了比 Java 更丰富的现代语言特性,进一步提升了开发者的使用体验。

在数值计算类程序中,性能主要由两方面决定:对计算的优化程度和语言本身的运行开销。C/C++ 在数值计算领域表现出色,不仅得益于编译器优化,还在于语言本身几乎没有额外开销。对于自动内存管理的高级语言来说,如何消除高级语言功能带来的额外开销,是提升性能的关键。加上现代 CPU 的缓存机制,数据布局对性能也有重要影响。

MoonBit 实现了性能与开发体验的双重优势,归功于其科学、现代的顶层设计:

  • MoonBit 语言设计简洁,额外开销极小,数据结构布局紧凑,能够更好地利用现代 CPU 的缓存架构。

  • MoonBit 采用多层中间表示(IR)和全局优化编译架构,能有效消除泛型等高级功能的开销,并对数据布局进行全局优化,以最大化性能。

内存管理:性能优于 Java & Swift

数值计算类任务能很好反映编程语言的基础性能。但在应用类程序中,更常见的是对内存中数据结构的各种操作。得益于定制的内存管理系统,MoonBit 的 native 后端在内存操作性能与内存占用上表现也非常优秀,优于拥有高性能 GC 的 Java,与同样编译到二进制、自动管理内存的语言 Swift:

memory-benchmark.jpg

上述 benchmark 来自论文 :Perceus: Garbage Free Reference Counting with Reuse,通过对语言在大量动态内存分配操作时的运行时间内存占用情况进行比较,从而很好反映语言的内存管理系统的性能差异。

在嵌入式与 IoT 设备上,计算资源与内存资源都非常紧张。因此编程语言的运行性能与内存占用表现都必须足够好,才能胜任嵌入式、IoT 开发的场景。

实践中,受制于硬件资源,嵌入式、IoT 开发者往往不得不使用 C/C++ 等底层语言来获得足够的性能。但这会极大增加开发者的心智负担、降低开发效率。而 MoonBit 原生提供了一种新的可能性:使用 MoonBit 原生开发程序,能兼顾开发体验、运行性能与内存占用

深度硬件集成,接入嵌入式与 IoT 生态

原生后端的另一个优势就是深度硬件集成。原生应用能直接访问设备的硬件功能,如摄像头、GPS、传感器等,对后端服务至关重要。且深度硬件集成可以提供更快速的响应时间和更丰富的功能,增强用户体验。

除了生成开发机器的原生二进制,MoonBit 的原生后端还能交叉编译到所有 C 支持的平台,在第一时间实现了对 RISC-V 平台的原生支持,拥抱嵌入式与 IoT 生态。

MoonBit 的原生后端能生成只有少量 libc 依赖的可执行二进制。因此,MoonBit 的原生后端非常适合用于编写嵌入式、IoT 程序。此外,由于 MoonBit 原生生成的二进制高度self-contained,部署 MoonBit 原生程序到各类边缘设备上非常便捷。

下面是一个使用 MoonBit 交叉编译到 RISC-V 的例子。为了获得开箱即用的交叉编译体验,首先需要安装 Zig。接下来,我们创建一个新的项目:

moon new hello-riscv
cd hello-riscv

为了交叉编译到 RISC-V,我们需要修改 src/main/moon.pkg.json 文件,让 MoonBit 使用 zig cc 来进行 原生后端的交叉编译。打开 src/main/moon.pkg.json,将其修改为:

{
"is-main": true,
"import": [
"username/hello/lib"
],
"link": {
"native": {
"flags": [ "-cc", "zig cc -target riscv64-linux-musl -O2 -static" ]
}
}
}

接下来,只需要执行:

moon build --target native

就能获得 target/native/release/build/main/main.exe,一个 RISC-V 二进制可执行文件。可以用 RISC-V 虚拟机或物理机直接运行这个可执行文件。这里以 Linux 下 libriscv 提供的虚拟机为例:

$ rvlinux ./target/native/debug/build/main/main.exe
Hello, World!

复用底层生态,同时提供安全可靠的系统编程体验

以C/C++语言为核心的原生生态在整个软件体系中占据着至关重要的地位。操作系统的底层 API、各类高性能计算库、以及 AI 张量库(如 llama.cpp、ggml 等),均通过 C ABI(应用二进制接口)提供服务。MoonBit的原生后端能够无缝与基于 C ABI 的第三方库进行交互,为MoonBit解锁更多开发场景,并带来卓越的用户体验。

MoonBit 原生后端采用多层 IR(中间表示)设计,其中最底层的 IR 是 C 语言的一个子集。这一设计确保了 MoonBit 与 C 语言之间的良好互操作性,避免了昂贵的 FFI(外部函数接口)开销,充分利用现有的各类高性能 C 语言库资源。未来,MoonBit 将提供规范化的 C 语言调用约定,进一步扩展其生态,便捷地集成现有的开源 AI 推理库,广泛应用于高性能计算和 AI 边缘推理领域,或用于开发直接调用操作系统 API 的高性能应用与服务。

相较于 C/C++ 等系统编程语言,MoonBit 在保持高性能的同时,尤为注重内存安全与可靠性。与 Rust 的内存安全机制不同,MoonBit 通过编译器自身的优化策略,在不影响开发者体验的前提下,在大多数开发场景中移除垃圾回收(GC)带来的开销。同时,针对部分核心场景,MoonBit 将逐步引入如“modality”等业界最新的研究成果,以提升程序的确定性。这一机制有效降低了学习曲线,使更多开发者能够轻松上手并从 MoonBit 生态中获益。

下一步

MoonBit 原生后端完成了 MoonBit 生态的最后一块拼图。通过原生后端,WebAssembly,JavaScript后端的支持可以满足绝大多数开发者的多场景需求。未来 MoonBit 团队将会聚焦于开发者的用户体验,提高软件的可靠性,以及培养开发者生态。在开发者体验方面值得注意的是,得益于 MoonBit 编程语言工具链的模块化设计,这三个后端共享了前面绝大部分基础框架,大部分前端的优化都会使三个后端的用户受益。

更多资源:

moonbitlang/x 现已支持文件 I/O

· 阅读需 4 分钟

cover

最近社区对于在 MoonBit 中进行文件 IO 的呼声很高,为此,我们在 moonbitlang/x 中提供了一个基础的 fs 包,moonbitlang/x/fs 包支持 wasm, wasm-gc, js 后端,包含以下常用 api:(注:此库目前是实验性的,其 api 可能会有进一步的更新和调整。)

  • write_string_to_file, write_bytes_to_file
  • read_file_to_string, read_file_to_bytes
  • path_exists
  • read_dir
  • create_dir
  • is_dir, is_file
  • remove_dir, remove_file

用法示例

  • 声明依赖

在命令行中执行以下命令

moon update
moon add moonbitlang/x

或者手动在 moon.mod.json 中声明依赖

"deps": {
"moonbitlang/x": "0.4.10"
}
  • 在需要使用的包中的 moon.pkg.json 导入依赖
"import": [
"moonbitlang/x/fs"
]
test "write_and_read" {
let path = "1.txt"
// write content to the file
@fs.write_string_to_file(
path="1.txt",
content=
#|target/
#|.mooncakes/
#|
,
)
// make sure the file has been create
assert_true!(@fs.path_exists(~path))
let byte = @fs.read_file_to_bytes!(~path)
// verify file content
inspect!(
byte,
content=
#|b"\x74\x61\x72\x67\x65\x74\x2f\x0a\x2e\x6d\x6f\x6f\x6e\x63\x61\x6b\x65\x73\x2f\x0a"
,
)
@fs.remove_file!(~path)
assert_false!(@fs.path_exists(~path))
try {
@fs.read_file_to_string!(~path) |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`1.txt` does not exist")
_ => return
}
let bytes = Bytes::from_array([65, 97].map(fn(x) { x.to_byte() }))
@fs.write_bytes_to_file(~path, content=bytes)
assert_true!(@fs.path_exists(~path))
let content = @fs.read_file_to_string!(~path)
inspect!(content, content="Aa")
@fs.remove_file!(~path)
assert_false!(@fs.path_exists(~path))
try {
@fs.remove_file!(~path) |> ignore
} catch {
@fs.IOError::NotFound(_) as e =>
inspect!(e, content="`1.txt` does not exist")
_ => return
}
}

更多示例可参见 fs/fs_test.mbt,这个文件是对于 fs 库的黑盒测试,其中的 test case 直观地展示了外部用户应该如何该库。

Background:MoonBit 项目的测试机制

目前一个 MoonBit 项目中可以有三种类型的测试:白盒测试(white box test)、黑盒测试(black box test)、内部测试(inline test)。

白盒测试

写在 *_wbtest.mbt中,构建系统会把当前包中的 *.mbt*_wbtest.mbt 一起打包编译,因此在 *_wbtest.mbt 中可以访问当前包的私有成员,这些测试可以使用 moon.pkg.json 中 import 和 wbtest-import 字段中的依赖。wbtest-import 只在白盒测试中用到,不会打包到最终的构建产物中。

黑盒测试

写在 *_test.mbt 中,构建系统会在编译 *_test.mbt 时会自动将其所在的包作为依赖, *_test.mbt 只能访问其所在包的公开成员(即模拟外部用户在使用这个包时的视角),这些测试可以使用 moon.pkg.json 中的 importtest-import 字段中的依赖(以及其所在包,不需要显式写在 test-import 字段中)。test-import 只在黑盒测试中用到,不会打包到最终的构建产物中。

内部测试

可以直接写在 *.mbt​(注意这里的*.mbt 不包含上面提到的 *_wbtest.mbt*_test.mbt​)中,可以访问当前包的私有成员,这些测试只使用 moon.pkg.json 中 import 字段中的依赖。

测试类型文件拓展名访问权限依赖来源打包到最终产物
白盒测试*_wbtest.mbt当前包私有成员moon.pkg.json import & wbtest-import
黑盒测试*_test.mbt公有成员moon.pkg.json import & test-import
内部测试*.mbt当前包私有成员moon.pkg.json import

更多资源

硬件实现:在ESP32-C6单片机上运行MoonBit WASM-4小游戏

· 阅读需 6 分钟

cover

本次2024 MoonBit全球编程创新挑战赛,我们开设了使用WASM-4开发的游戏赛道,在《基于 Wasm4 框架的 MoonBit 游戏开发指南》一文中,我们介绍了如何使用MoonBit在WASM-4框架中编写小游戏。

WASM-4模拟的硬件规格主要包括:160x160 像素的显示屏;64 KB 的线性内存;支持键盘、鼠标、触摸屏,以及最多 4 个游戏手柄作为输入设备;音频输出;1KB的存储空间。一个自然的想法便是将WASM-4移植到单片机上运行,将模拟的IO设备替换为真实的硬件。本文,我们将探索如何在真实的硬件中运行这些由MoonBit编写的小游戏。本文使用的芯片为 ESP32-C6,它搭载 RISC-V 32 位单核处理器,主频高达 160 MHz,内置 320 KB ROM,512 KB SRAM。

如下动图展示了在 ESP32-C6 开发板上运行使用MoonBit编写的贪吃蛇游戏。

hardware.gif

移植过程

WASM-4 项目仓库中提供了两种运行时环境:一种是在Web环境中运行的,另一种是本地(native)运行时。所谓WASM-4运行时指的是一种能够运行符合WASM-4规范的游戏环境。 其中,本地运行时则是使用C语言实现的,只需要对其稍加修改、删除不需要的组件,便能在单片机上运行。

LCD显示屏

WASM-4 的本地运行时自带图形用户界面 (GUI),用于在屏幕上显示游戏图像。然而,当我们把它移植到硬件上时,就不再需要这个图形界面了,因为我们可以用真实的LCD屏幕来代替它。

因此,第一步是删除与 GUI 相关的组件。WASM-4 有一个内部的帧缓冲器,用于暂存显示数据,由 MiniFB 负责渲染每一帧。我们只需要保留这个缓冲区用于存储图像数据,同时移除 MiniFB 相关代码。在单片机上调试之前,我们可以将帧缓冲器中的数据输出为图片文件。一旦确认图像无误,我们的任务就变得非常简单了,只是将一个存有像素点数据的数组显示到显示屏上。本文所选用的显示设备为一块由ST7789驱动的1.54英寸LCD,其分辨率为240*240,如下图所示:

lcd.png

因此在居中显示WASM-4 160*160的图像时,四周会留有40像素的空间。此外还需注意,由于本文所选用的LCD显示屏只支持RGB565颜色格式,因此可能会出现颜色偏差。

游戏手柄

WASM-4支持键盘、鼠标、触摸屏,手柄等输入设备。本文使用硅胶按钮模拟手柄。

console.png

手柄有上下左右四个方向按键,以及X和Z两个动作按键。WASM-4使用其线性内存中的1个字节来表示一个手柄状态,其中的第 0 位和第 1 位分别对应 X 和 Z 按钮,第 4 到第 7 位对应方向键的左、右、上、下。每一位的值表示相应按钮是否被按下。因此只要为每个按钮的按下按键事件中设置对应的比特位即可。本文使用最简单的方式,每个按键占用一个GPIO接口。

使用 WAMR 替换 wasm3 解释器

由于 wasm3 项目不再活跃维护,缺失了一些需要的特性。因此,我们还需要将 WASM-4 本地运行时的 wasm 执行引擎为替换为 WAMR。与wasm3类似,我们只需要在WAMR的运行环境中提供相同的FFI即可。例如用于绘制线段的 linehlinevline,用于绘制矩形的 rect,用于贴图的 blitblitSub等。

总结

至此,本文已经完成了基本的游戏显示与控制功能,点击查看完整代码。当然,仍然有很多有趣的方向值得探索,例如本文并未充分利用ESP32芯片丰富的无线接口,我们可以利用WiFi实现WASM游戏模块的热加载。此外,本文未实现外接扬声器以支持音频输出。

更多资源

通过 Wasm 组件使用 MoonBit 编写 AI 代理

· 阅读需 5 分钟

cover

如果你有关注MoonBit的仓库,最近你可能发现了一个叫做 "peter-jerry-ye-code-review" 的机器人对每个新增的 PR 发表评论。如果有新的提交,它还会自动对评论进行更新。没错,这个实验性代码审查机器人就是我们用 MoonBit 编写的AI代理。在这篇文章中,我们将解释它的工作原理。

screenshot.png

你可以在这个仓库,中找到完整代码的快照,README 文件里有技术细节的详细说明。

使用场景

当有 Pull Request 提交时,我们都希望能够得到及时的审查。这时,如果可以实时提供一些初步检查反馈,便可以提高审查效率,以便开发者及时更新内容。

一个自动审查 PR 的机器人可以轻松地帮助我们实现这一目标。通过使用场景分析我们可以推断出这是一个无服务器任务:服务器接收到 Pull Request 的通知,获取必要的信息,然后发布评论。过程中无需存储任何数据。出于 WebAssembly 对无服务器任务场景的优势,尤其在冷启动中极快的速度,在本次任务中我们将选择使用 MoonBit 和 Wasm 组件模型(component model)实现项目。

工作原理

架构

本示例将基于开源运行时 Spin 开发。Spin 建立在 wasi-http 标准上,并提供了连接 Redis 或关系型数据库的接口。本次示例将部署在 Fermyon Cloud,该平台为初学者提供免费计划。你也可以选择开源的 Spin Kube,并在 Kubernetes 集群上自托管。

通过 wit-bindgen 对 MoonBit 的支持,我们可以通过获取 WIT 文件并生成绑定代码,轻松集成 Spin 的功能。

工作流程

下图展示了工作流程:

在创建 GitHub App 并由用户安装后,我们可以接收 Webhook 事件。GitHub 会根据配置发送 Webhook 事件。接收到负载后,我们可以验证并收集必要信息:仓库名称、安装 ID、Pull Request 编号以及事件类型。

通过安装 ID,我们可以为 App 获取访问令牌,使其能够访问用户授权的权限,如读取/写入 Pull Request 权限,特别是如果应用安装在私有仓库中。

我们可以根据事件类型决定采取的行动。例如,"opened" 表示新 PR,"synchronize" 表示现有 PR 的更新。对于前者,我们可以创建新的评论,而对于后者,我们更新现有评论。相关事件类型和负载在 Webhook 文档中有详细描述。

通过负载中的信息,我们可以从 GitHub 获取 Pull Request 的变动情况,例如以 diff 或 patch 格式获取更改。我们还可以列出提交并获取相应的更改和消息等。

最后,我们将收集的信息通过 HTTP 请求发送到我们选择的 LLM 提供商,使用其响应内容在 Pull Request 上创建或更新评论。

结论

MoonBit 在支持组件模型后,可以适用于多种应用开发场景。随着越来越多的运行时对该标准的支持,MoonBit 将广泛应用于开发无服务器应用或边缘计算应用。本文介绍的 AI 代理只是其中一个示例,未来会有更多应用场景出现。

Additional resources:

MoonBit 发布 beta 预览版,快速实现多领域落地应用

· 阅读需 18 分钟

cover

AI时代下编程语言的战略机遇

这个世界是否还需要一门新的编程语言?毫无疑问每一门新编程语言诞生时都被这样质问过,我们相信 MoonBit 可以通过技术实力给你肯定明确的答案。

编程语言的发展经历了60年代:结构化编程的兴起,到70年代:面向对象编程与C语言,再到90年代:脚本语言与互联网 ,又转变为00年代:语言的统一与多样性,最终来到了10年代:现代语言与大数据;编程语言在不断进步,同时也有大量的编程语言慢慢销声匿迹。

大模型时代的横空出世,把万事万物席卷进这股浪潮,编程语言作为计算机核心组件与软件开发者创造技术世界最重要的工具,自然被顶上风口浪尖,整个技术行业都对编程语言提出了更高的要求。

在大模型时代,工具重新塑造了开发者的工作方式,同时也降低了学习新语言的门槛,开发者将更专注于创意和设计,代码的实现和优化将由编程工具完成。这一转变推动编程工具从简单的代码生成器进化为复杂问题解决平台。

MoonBit AI 云原生开发者平台(MoonBit 编程语言及工具链)就在这个机遇与挑战并存时代应运而生,整个开发者工具链于 AI高度垂直整合,应用场景聚焦于云计算和边缘计算,MoonBit 目标是重塑软件开发生态。

MoonBit 于 2022 年启动开发计划,凭借团队在 ReScript、OCaml、Flow等工业编程语言研发中的十多年经验,迅速在两年内完成了整套工具链的开发,致力于成为第一个走向国际的中国编程语言。

Beta预览版的标志性意义

自从去年8月18日 alpha 版的宣布带来了热烈的反响,经过整整一年的高速迭代,MoonBit 迎来了 beta 预览版这样一个重要的里程碑: 我们在核心语言特性上媲美甚至超越了大部分主流语言 1.0,整个语言特性的开发趋于稳定,可以吸引开发者更广泛的参与到生态建设中来。这里列举几个 MoonBit 核心且有挑战性的特性:

  • 现代化的泛型系统

现代语言复杂性的很大一部分在于类型系统的设计,很多主流工业语言,比如 JavaGo 在 1.0 版本推出很多年以后才会渐渐完善泛型的支持,但是这会对生态造成撕裂。MoonBit 在 beta 版本已经完成了泛型和 ad-hoc polymoprhism,而且 MoonBit 在保持极快的编译速度的同时,提供了零开销的泛型支持。

  • 准确的错误处理

错误处理是现代语言设计中的重要组成部分,大部分编程语言在静态分析中,彻底放弃错误处理的支持,这意味着任何函数都可以抛出异常,很难写出高可靠性的代码,MoonBit 通过准确的控制流分析,可以完全在编译时期跟踪函数的错误。这一过程几乎完全由编译器自行推导,而又不像 Java checked exception 那样过于繁琐,对用户带来较高的心智负担。

  • 高效的迭代器

传统的编程语言的迭代器会产生频繁的装箱操作,导致其性能比普通的循环要糟糕很多,MoonBit 创新性的使用了零开销的迭代器设计,可以让用户既能写出优雅的代码,也能保持程序的性能。

四大优势

2023 年 8 月 18 日,MoonBit首次在海外发布后,迅速登上了 HackerNews 的头条。此外,MoonBit 还在全球 DevOps 领域权威媒体平台 The News Stack日本顶尖科技社区 Zenn 等知科技媒体上频频出现,进一步巩固了其全球影响力。

在这一过程中,MoonBit 一直致力于发挥其在语言速度、代码精简、安全保障和数据处理等方面的卓越优势。

MoonBit 旨在实现全栈的高效,包括编译性能和运行时性能。MoonBit 为多级中间表示(IR)上的全局优化而设计,这种方法改善了内存布局,最小化缓存未命中率,并提供了优越的数据和控制流分析环境。从编译速度来看,MoonBit 编译 626 个包(package)只需 1.06 秒,比 Rust 快了近 9 倍,运行时间仅为Go的1/6

MoonBit在输出Wasm代码体积上相较于传统语言显著优势,MoonBit 通过 WebAssembly(Wasm)组件模型,在生成代码时实现了显著的体积优化。相比其他编程语言,MoonBit 编写的 Wasm 组件代码体积大幅减少。例如,在编译一个简单的“Hello World” HTTP 服务器时,MoonBit 的输出文件大小仅为 27KB,而 WasmCloud 提供的http-hello-world模板中 Rust 的输出为 100KB,TypeScript 为 8.7MB,Python 更是高达 17MB。

安全

借助前文提到的创新错误处理机制,MoonBit 有效解决了传统编译技术在错误处理机制转换中遇到的适用范围有限和内存开销大的问题。通过静态分析源程序中的错误处理代码并根据目标编程语言的特性进行转换,MoonBit 扩大了适用语言范围,减少了内存开销,同时提升了代码的安全性和稳定性。

健壮的类型系统是 MoonBit 对安全保障的重要设计之一。MoonBit 采用了强大的类型系统,并内置静态检测工具,在编译期检查类型错误,从而提高代码的正确性和可靠性。

MoonBit 对安全的保障也来源于对 WebAssembly 的原生支持。MoonBit 充分利用 Wasm 对沙箱环境,内存隔离、默认的无权限机制(deny-by-default)等安全架构的设计,释放其在云计算和边缘计算安全问题上的潜力;同时结合语言自身类型系统安全,删减无用代码(DCE)、编译为经过验证的 Wasm 指令等三层保障,确保代码可信与数据隐私安全。

专注于数据处理

MoonBit 作为一门多范式编程语言,在保持语言简单性的同时,也侧重于提供数据处理的最佳体验。MoonBit 通过原生支持 Json 处理、Iter 类型和模式匹配实现高效数据处理,在语法设计上兼具动态语言的灵活性和静态语言的安全高效,直观而简洁地进行对数据的解析和转换。

json

MoonBit 在数据处理上的语法设计旨在优化常见的数据处理场景,解决传统方法中由于生成多个中间数组而导致的性能问题,在 Iter 性能对比中,MoonBit 的数据处理速度达到了 JavaScript 的 25 倍之多

AI 原生 IDE

相较于传统的编程语言设计路线,MoonBit 在战略上从一开始就提供全套开发、调试、部署一站式解决方案。MoonBit 不仅提供了多后端支持、通用程序语言设计,还涵盖了编译器、构建系统包管理集成开发环境(IDE)、调试器,部署工具等。这个全面的设计使得 MoonBit 能够实现高度垂直整合,而且可以同时在云端和边缘端执行,更好地与各种资源进行交互,从而为用户提供极致的开发体验和性能。

MoonBit 提供对 IDE 的支持不仅包含了现代 IDE 的所有功能,与其他 Cloud IDE 不同,MoonBit 无需依赖容器,快速语义分析技术可处理大量的代码库,并且在更大规模的代码库中也能在百毫秒级别完成 IDE 响应。

现今大多数编程语言的 IDE 是为桌面操作系统设计的,未能很好地适应云原生环境和边缘端的需求。MoonBit 则通过采用高度并行化的架构和对分离编译的原生支持,突破这一限制,使得 Cloud IDE 可以直接在边缘端运行。这在现今大多数语言的 Cloud IDE 中是首次。

除传统 IDE 的功能以外,MoonBit AI 助手现已内置于 MoonBit IDE,并实现了自动化测试生成、文档生成,以及代码解释功能,为应用的开发、测试和文档编写提供了全方位的支持,使得开发者能够更加专注于业务逻辑的实现,而不必担心底层的复杂性和维护工作。

MoonBit在初始阶段已经提供相对于其他语言通常在成熟阶段才拥有的调试工具。并在JavaScript后端实现开箱即用的调试功能,用户仅需在 JavaScript Debug Terminal 上执行 moon run --target js --debug 即可立刻进入调试。

应用场景

MoonBit 是一个全场景覆盖的开发者平台,紧跟科技前沿和行业趋势,侧重于云计算、边缘计算、AI 以及教学领域的发展,并在每个领域做到极致,确保在性能上领先其他语言至少一个数量级。多应用场景:

云计算

在云计算领域,MoonBit 近期已支持 Wasm 组件模型(component model),并有海外用户实现了与 Spin 框架集成的 MoonBit SDK。通过将应用程序拆分成独立的、可重用的组件,MoonBit 可以在云计算开发中更好地利用模块化开发、灵活的部署和更新、资源优化、防止单点故障提高安全性、易于扩展和集成等特性,实现云计算应用场景中的突出优势,提高系统的灵活性和安全性,简化扩展和集成过程,从而显著提高云计算开发效率并降低运营成本。

边缘计算

在边缘计算领域,目前已有 MoonBit PDK 插件,并被 Extism 收入官方 PDK 库。通过 PDK 的支持,MoonBit 能够在边缘计算应用中更高效地利用硬件资源,实现分布式计算和本地处理,提升性能和响应速度,同时确保设备兼容性和数据安全性,从而大大提升开发和部署效率,满足低延迟和高效能的需求。

教育(wasm4, mario, minimoonbit)

在 MoonBit 的应用生态发展中,我们同样注重为学生群体以及编程语言初学者提供简单易学的入门环境,并以游戏的形式打造轻松愉悦的学习体验。

MoonBit 现已支持使用 WebAssembly 开发小型复古游戏的 WASM-4 框架。凭借 MoonBit 的高效工具链和简洁设计,入门级开发者可以在官方教程的指导下轻松创作属于自己的第一款游戏。同时,框架内置的多玩家支持功能能够让至多四名玩家共同体验游戏的乐趣。无论是创作过程还是开发体验,MoonBit 都能让初学者体验前所未有的简单与高效。

不仅如此,由于 MoonBit 支持实时可视化开发,同时编译速度极快,开发者还能够实时更改代码来灵活调整游戏参数。

MoonBit 团队正在开发 MiniMoonBit 这一编译器教学项目。学生通过这个项目可以用 MoonBit 实现 MoonBit 语言的一个子集。它去除了多态等复杂的功能,同时学生也可以在简单的语言设计下深入了解类型推导、高阶函数等现代语言特性。由于 MoonBit 强大的表达力,相较传统的用 C/C++ 实现的编译器项目,学生可以更轻松地上手实现。同时,MiniMoonBit 项目采用了模块化的设计,多层 IR 都有对应的 Json 文本表示,学生不再需要从头实现编译器的每个环节,而是可以选择感兴趣的模块来学习。

未来计划与支持

MoonBit AI云原生开发者平台是一个“道阻且长,行则将至”的系统工程,开发编程语言工具链以及培养生态系统是一个漫长且耗费各项资源的过程,但值得骄傲的是 MoonBit 汇聚了一批非常年轻的技术精英。当然,仅靠自身的力量是很难走向巅峰,我们正在马不停蹄的扩大 MoonBit 社区力量,培养社区人才,2 年的时间里 MoonBit 工具链已有几百名社区贡献者,近万人的全球的用户,我们相信未来 MoonBit的 一定会成为百万级用户的现象级编程语言。

目前,MoonBit 已支持 Wasm 与 JS 后端,未来,MoonBit 计划支持 native 后端。这意味着无论是 UI、客户端、边缘计算还是系统编程,用户都能在 MoonBit 平台上找到适合自己的使用场景,从而使生态能够覆盖所有可能的应用场景。

路线图

使用 MoonBit 开发 Wasm 组件模型

· 阅读需 9 分钟

cover

Wasm组件

WebAssembly(Wasm)是一种新的低级虚拟指令集标准(low-level virtual instruction set standard),用于沙箱模型。低级的,意味着它接近原生速度。虚拟的,意味着它可以在包括浏览器和操作系统在内的多个运行时(runtime)上运行,例如wasmtimewamr。它是沙箱模型,这意味着它不能与外界交互,除非使用FFI。不过FFI只能返回数字,因此通过线性内存进行数据传输是更有效的方法。许多编程语言都可以编译成Wasm,包括Java、JavaScript/TypeScript、Python、Rust以及当然还有MoonBit

那么如何结合用不同编程语言实现的Wasm组件呢?我们便需要引入组件模型(component model),一个统一接口的提案。通过组件模型,我们可以定义一个高级抽象的API。只要接口匹配,组件就可以与不同组件结合。

本文将介绍如何使用MoonBit编写一个输出“Hello World”的小型HTTP服务器。通过本教程我们可以看出,MoonBit在开发Wasm组件模型时实现了高兼容性和互操作性,同时能够显著减少输出大小。

操作步骤

我们将编写一个小型HTTP服务器,它将使用MoonBit打印“Hello World”。先决条件如下:

定义WIT(Wasm Interface Type)

首先,你需要使用WIT定义接口(如何使用详见官方手册)。

wit/deps.toml中指定依赖项。本教程中仅使用wasi-http版本0.2.0。

http = "https://github.com/WebAssembly/wasi-http/archive/v0.2.0.tar.gz"

使用wit-deps更新依赖项,在wit/deps文件夹中可以看到所有依赖项。

然后我们在wit/world.wit中指定“世界”对应于生成的Wasm:

package moonbit:example;

world server {
export wasi:http/incoming-handler@0.2.0;
}

一个“世界”可以包含其他“世界”,或导入/导出接口。这里我们导出wasi:http版本0.2.0的incoming-handler接口,因为HTTP服务器需要导出一个传入处理程序接口,以便运行时可以使用它来处理传入请求并生成响应。

生成代码

在这一步骤,我们会使用wit-bindgen生成代码。你可以利用这个命令安装:

cargo install wit-bindgen-cli --git https://github.com/peter-jerry-ye/wit-bindgen/ --branch moonbit

获得wit-bindgen命令后,只需使用适当的子命令(moonbit)和WIT文件的位置(wit)执行它。还有参数用于指定类型是否应派生Showtrait 或Eqtrait。

wit-bindgen moonbit wit --derive-show --derive-eq --out-dir .

你将获得以下内容:

.
├── ffi
│ ├── moon.pkg.json
│ └── top.mbt
├── gen
│ ├── ffi.mbt
│ ├── interface_exports_wasi_http_incoming_handler_export.mbt
│ ├── moon.pkg.json
│ └── worlds_server_export.mbt
├── interface
│ ├── exports
│ │ └── wasi
│ │ └── http
│ │ └── incomingHandler
│ │ ├── moon.pkg.json
│ │ ├── README.md
│ │ └── top.mbt
│ └── imports
│ └── wasi
│ ├── clocks
│ │ └── monotonicClock
│ │ ├── moon.pkg.json
│ │ ├── README.md
│ │ └── top.mbt
│ ├── http
│ │ └── types
│ │ ├── moon.pkg.json
│ │ ├── README.md
│ │ └── top.mbt
│ └── io
│ ├── error
│ │ ├── moon.pkg.json
│ │ └── top.mbt
│ ├── poll
│ │ ├── moon.pkg.json
│ │ ├── README.md
│ │ └── top.mbt
│ └── streams
│ ├── moon.pkg.json
│ ├── README.md
│ └── top.mbt
├── moon.mod.json
├── wit // contents ignored here
└── worlds
└── server
├── import.mbt
├── moon.pkg.json
└── top.mbt

生成的项目有四个文件夹:

  • ffigen是生成的帮助Wasm绑定的文件,可以忽略。gen目录包含项目入口。

  • interface包含所有导入到所选“世界”的接口。分为importsexportsimports提供所有导入的函数和类型,而exports包含你所要导出的函数以及一个空实现(panic())。

  • worlds包含"世界"。与interface类似,它包含一个import.mbt,提供“世界”级别的导入函数和类型,以及一个top.mbt,包含导出函数的模板。

然后你可以像开发一般MoonBit应用一样继续开发。此时moon check --target wasm应该能够成功通过。你可以通过运行moon doc --serve查看API以及类型或函数的注释文档。别忘了执行moon fmt来格式化程序。

开发

以下是我们用于演示的实现最小输出的“Hello-World”服务器代码:

pub fn handle(
request : @types.IncomingRequest,
response_out : @types.ResponseOutparam
) -> Unit {
let response = match request.path_with_query() {
None | Some("/") => make_response(b"Hello, World")
_ => make_response(b"Not Found", status_code=404)
}
|> Ok
response_out.set(response)
}

fn make_response(
body : Bytes,
~status_code : UInt = 200
) -> @types.OutgoingResponse {
...
}

完整示例见moonbit-docs/examples/wasi-http

组件化

我们已经实现了一个核心Wasm,即一个遵循WebAssembly标准的Wasm。然而,我们需要将其转变为一个组件,以便可以将必要的信息——接口——一并分发。

你需要使用wasm-tools将核心Wasm嵌入到组件中。首先将WIT信息嵌入到核心Wasm的自定义部分中,此步骤需要指定编码为UTF-16。然后我们将核心Wasm转换为组件Wasm。

moon build --target wasm
wasm-tools component embed wit target/wasm/release/build/gen/gen.wasm -o target/wasm/release/build/gen/gen.wasm --encoding utf16
wasm-tools component new target/wasm/release/build/gen/gen.wasm -o target/wasm/release/build/gen/gen.wasm

如果你更喜欢使用npmpnpm,也可以使用JCO来完成此步骤。

使用

利用我们创建的Wasm,可以使用Wasmtime进行托管:

wasmtime serve target/wasm/release/build/gen/gen.wasm

你也可以使用JCO在Node.js或Deno上进行服务,或者使用WasmCloudSpin进行托管。

比较

compare

至此,我们已经实现了一个简单的仅输出 “Hello World” 的 HTTP 服务器。下表为 MoonBit 与主流编程语言生成的http-hello-world大小对比(基于WasmCloud的模板)

语言输出尺寸
Python17M
TypeScript8.7M
Rust100K
MoonBit27K

结论

我们展示了如何使用MoonBit创建一个遵循组件模型标准的Wasm。组件模型为创建可互操作的Wasm提供了新标准。比如我们能够通过从Spin中提取WIT文件,在5分钟内轻松构建一个无服务器AI应用。

通过支持WebAssembly组件模型,MoonBit增强了其在微服务架构和云原生应用中的应用场景,具有高编译性能和紧凑代码尺寸,允许在各种环境中快速部署和执行。

在8月18日,MoonBit将达到beta预览版本,表明着我们在语言方面已达到一定的稳定性,适合投入更加广泛的测试与实际应用环境。未来,我们将继续拓展MoonBit生态系统,优化文档和工具链,以提供更好的用户体验。敬请期待!

你还能做什么

基于 Wasm4 框架的 MoonBit 游戏开发指南

· 阅读需 10 分钟

MoonBit 即将面向全国举办“编程创新挑战赛”,并包含游戏赛道。本教程将介绍本次比赛中使用的框架 Wasm4,以及如何使用 MoonBit 在 Wasm4 框架中编写游戏。相关赛事详情见文末。

cover

如果你曾访问过 mooncakes 或我们官网的,你可能已经注意到我们发布了一个名为 wasm4的包以及一个新的演示项目 Wasm4 Snake。今天我们将向大家介绍这个出色的游戏开发框架,并演示如何使用 MoonBit 开发。

什么是 Wasm4

WASM-4 是一个使用 WebAssembly 构建复古风格游戏的框架。它提供了一个 160 x 160 像素的游戏主机,内存少于 64K。通过使用 WebAssembly 这一指令集的新标准,这些游戏能够在所有网页浏览器上运行,甚至可以在一些低端设备上运行。任何能够编译为 WebAssembly 的编程语言都可以用于开发游戏。随着 MoonBit Wasm4 SDK 的发布,现在开发者也能使用 MoonBit 轻松开发 Wasm4 游戏。

本教程将详细介绍如何使用 MoonBit 制作你的第一款 Wasm4 小游戏。

开发教程

MoonBit 库支持实时重载,你可以在我们的云 IDE 中试玩官方提供的 Wasm4 贪吃蛇游戏。

通过 MoonBit 提供的云 IDE,你可以直接在 gallery 上体验和开发游戏,无需任何安装步骤

gallery: https://www.moonbitlang.cn/gallery/wasm4-snake/

在您在本地端开发,你需要安装:

创建项目

在当前目录下使用 MoonBit 创建一个新项目,并使用 npm 安装 wasm4

moon new --user moonbit --name demo --lib --path .
npm install -D wasm4

完成后,你将看到如下的目录结构(node_modules 目录未列在内):

.
├── .gitignore
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── moon.mod.json
├── moon.pkg.json
├── package-lock.json
├── package.json
├── README.md
└── top.mbt

其中 moon.mod.json 定义了整个项目的配置,moon.pkg.json 则定义了每个包的配置。top.mbt 将作为游戏的主要入口文件,我们可以在 lib 目录中编写辅助函数,而 hello_test.mbt 提供了一个黑箱测试的示例。在这个示例中,我们将不使用 lib 目录。

添加 Wasm4 依赖

在创建项目后,我们需要添加 moonbitlang/wasm4依赖:

moon update && moon add moonbitlang/wasm4

这将导致 moon.mod.json 变更为如下内容(以当前版本为例):

{
"name": "moonbit/demo",
"version": "0.1.0",
"deps": {
"moonbitlang/wasm4": "0.2.0"
},
"readme": "README.md",
"repository": "",
"license": "Apache-2.0",
"keywords": [],
"description": ""
}

同时,还需要将 moon.pkg.json 修改如下,以满足要求:

{
"import": [
"moonbitlang/wasm4"
],
"link": {
"wasm": {
"exports": [
"start",
"update"
],
"import-memory": {
"module": "env",
"name": "memory"
},
"heap-start-address": 6560
}
}
}

在这一步有几点需要注意:

  • 我们将 moonbitlang/wasm4/lib 包导入为 wasm4,所以需要使用 @wasm4 修饰符来调用函数和类型。

  • 我们将此包配置为 Wasm 后端的链接目标,并进行了以下设置:

    • 按照 Wasm4 的要求,导出 startupdate 函数。

    • 导入名为 env 模块中的 memory 内存,以符合 Wasm4 的 ABI 规范。

    • 为了与 Wasm4 的 ABI 兼容,将 MoonBit 的堆内存起始地址设为 6560。低于 6560(0x19a0)的内存空间将保留给 Wasm4 使用。

相应地,我们将修改 top.mbt

pub fn start() -> Unit {

}

pub fn update() -> Unit {

}

现在,可以通过以下命令执行:

moon build --target wasm
npx wasm4 run target/wasm/release/build/demo.wasm

如有需要调试并查看带有函数名的堆栈跟踪信息,也可以使用调试模式:

moon build --target wasm -g
npx wasm4 run target/wasm/debug/build/demo.wasm

a blank canvas

在这一步,浏览器会自动打开并显示游戏画面。接下来,让我们开始添加游戏内容吧!

示例:移动方块

首先,在屏幕上绘制一个方块:

pub fn start() -> Unit {

}

pub fn update() -> Unit {
@wasm4.set_draw_colors(index=1, 2)
@wasm4.rect(0, 0, 80, 80)
}

a green box appear on the top left corner of the canvas

现在,你应该看到这样的画面。Wasm4 提供了四个调色板和四种绘图颜色。根据具体的 API,将使用相应的绘图颜色。这里的操作是我们将绘图颜色 1 设置为第二个调色板的颜色,然后从位置 (0, 0)开始绘制一个 80 x 80 的矩形。请记住,在编程世界中,显示坐标的原点位于左上角,y 轴方向朝下。

moonbitlang/wasm4 提供了一个高级抽象,使你可以轻松编写代码。为了避免混淆,绘图颜色和调色板的索引从 1 开始。你还可以设置 160 x 160 像素中的每一个像素。有关这一步的设置请参阅官方 Wasm4 document 和 MoonBit 的 SDK API

现在屏幕上显示了一个静止的方块,但毕竟我们是在开发游戏,所以它最好是能动起来。start 函数将在初始化期间调用一次,而 update 函数将以 60Hz 的频率被调用。因此要让方块移动起来,我们可以这样编写代码:

struct Position {
mut x : Int
mut y : Int
}

let pos : Position = { x: 0, y: 0 }

pub fn update() -> Unit {
if pos.x + 80 <= 160 {
pos.x += 1
}
@wasm4.set_draw_colors(index=1, 2)
@wasm4.rect(pos.x, pos.y, 80, 80)
}

然后它将变成这样(实际的移动速度会比图上显示的速度更快):

a green box moving right

操纵方块

Wasm4 提供了两个按钮(XZ)以及四个方向按钮。让我们尝试让方块根据用户的输入移动吧!

pub fn update() -> Unit {
if @wasm4.get_gamepad(index=1).button_right && pos.x + 80 < 160 {
pos.x += 1
} else if @wasm4.get_gamepad(index=1).button_down && pos.y + 80 < 160 {
pos.y += 1
} else if @wasm4.get_gamepad(index=1).button_left && pos.x >= 0 {
pos.x -= 1
} else if @wasm4.get_gamepad(index=1).button_up && pos.y >= 0 {
pos.y -= 1
}
@wasm4.set_draw_colors(index=1, 2)
@wasm4.rect(pos.x, pos.y, 80, 80)
}

a green box moving under control

更多开发内容

在调试过程中,你可以使用 @wasm4.trace 将调试信息写入控制台。此外,按下 F8 键查看详细的运行状态,如之前的截图所示。

如果要发布游戏,可以运行以下命令生成一个独立的 HTML 页面:npx wasm4 bundle --html game.html target/wasm/release/build/demo.wasm。通过静态文件服务器,其他人就能够轻松体验你设计的游戏。

Wasm4 支持最多四名玩家同时联网游戏,而且无需额外配置。这意味着你可以轻松创建自己的多人蛇蛇大作战,并与朋友一起享受游戏乐趣!更多信息请参考 Wasm4 文档SDK API

总结

还等什么?使用支持实时重载的 MoonBit 库开始你的第一个 Wasm4 游戏吧,玩得开心!

如果对 Wasm4 框架下的游戏风格还不熟悉,访问 Wasm4 官方页面查看更多案例

MoonBit 全球编程挑战赛

由深圳市河套深港科技创新合作发展署指导,IDEA-MoonBit 联合名企一线互联网大厂、全国知名高校举办的 MoonBit 全球编程创新挑战赛,即将拉开帷幕!

在游戏赛道中,参赛者将通过使用 MoonBit 编程语言和 Wasm4 框架,开发出具有创新性和趣味性的游戏作品,展示他们的创造力和编程技巧。

在 MoonBit 全球编程挑战赛中,每位参赛者都有机会让自己的作品得到广泛的推广,创作优秀作品的参赛者将得到丰厚奖金与奖品,表现突出的参赛者更有机会获得知名企业提供的工作或实习机会,为你的职业发展添砖加瓦!

大赛的更多信息和报名细节,后续将通过 MoonBit 的官方平台发布,敬请期待~

更多资源

MoonBit 构建系统正式开源

· 阅读需 11 分钟

cover

作为由 AI 驱动的云服务和边缘计算开发者平台,MoonBit 自设计之初便注重工具链与语言的协同效果。MoonBit 为开发者提供了一套开箱即用的工具链,包括集成开发环境(IDE)、编译器、构建系统和包管理器,使开发者在使用 MoonBit 时能够轻松入门,极大提高开发效率、代码质量和项目的可维护性,减少繁琐的手动操作和配置管理,从而更加专注于开发的核心逻辑与创新,以提升整体开发体验和生产力。 MoonBit 的构建系统 moon 启动于 2023 年 5 月,今天已通过 GitHub moonbitlang/moon 公开,以 AGPL 协议开源。

moon:MoonBit构建系统

moon 是 MoonBit 的构建系统,为 MoonBit 项目提供编译构建,自动化测试工具(集成 expect test),覆盖率测试、包管理器等功能,此外,moon 作为 MoonBit 语言编译工具链的重要组件,与 IDE 紧密结合,为 IDE 分析代码提供了详尽的项目结构和依赖等信息。

moon 用 Rust 开发,得益于 Rust 语言的内存安全、高性能和并发处理能力、跨平台等特性,确保构建过程的稳定性和速度。

moon 的并行与增量构建功能得益于 n2 项目(n2 与 ninja 都是由 Evan Martin 创建,n2 相比 ninja 更加轻量,并且在增量构建方面更出色),对其修改部分将保持原项目的开源协议 moonbitlang/n2

为什么选择 moon

moon

  • 速度

    • MoonBit 的编译器速度极快,这得益于其精心设计的编译流程和优化策略。作为用户与编译器之间的桥梁,moon 在底层设计上力求精简,最小化自身的封装开销,从而最大化编译速度。

    • 此外,moon 还为集成开发环境(IDE)提供了详尽的项目结构和依赖等信息,这对于对延迟极为敏感的 IDE 场景至关重要。moon 通过优化核心构建步骤的性能,确保了即使在高度交互的开发环境中也能提供流畅的用户体验。

  • 并行增量构建

    • moon 得益于 n2 项目的并行增量构建功能是其高效性的关键所在。通过自动分析和理解各个构建任务之间的依赖关系,moon 能够智能地将独立的任务并行化处理,充分利用现代多核处理器的强大性能,从而显著加快构建速度。更为重要的是,moon 仅对自上次构建以来发生更改或其依赖项有所更新的文件进行构建,这种增量构建策略极大地提高了构建效率,使得 moon 能处理需要频繁构建的大型项目。
  • 集成和测试支持

    • 与自动化测试工具紧密结合,能够在代码提交和构建过程中自动执行单元测试、集成测试和端到端测试,确保每一行代码都经过严格的检验。

    • 在代码质量保障方面,MoonBit 提供了代码格式化和静态分析工具,这些工具能够自动检查代码风格的一致性,并识别潜在的逻辑错误和安全漏洞。在持续集成/持续部署(CI/CD)的流水线中,这些功能尤为重要,它们能够在代码合并到主分支之前,及时发现并报告代码质量问题,从而确保团队能够协同开发出高质量的代码。

Benchmark

构建矩阵性能测试

我们测试了 moon 在编译构建具有复杂依赖关系的项目时与 Rust cargo、Go 的性能差异。测试包括生成 DR _ DC 目录,即目录矩阵,每个目录包含 MR _ MC 模块,即模块矩阵。模块矩阵 r 行 c 列中的模块依赖于同一目录前一行中的所有模块。一个目录中的第一行模块依赖于前一行目录中的所有模块。这种依赖关系便于测试并行性,同一行中的模块可以并行编译,同一行中的目录也可以并行编译。如想进一步了解测试准则请参见 omake1,项目生成器代码见 moonbit-community/build-matrix

在我们的测试中,取 DR、DC、MR、MC 均为 6,加上 main 模块后,每个项目都包含 1297(6^4 + 1)个包。测试环境为 MacBook Pro Apple M3 Max 128G RAM,操作系统为 macOS 14.4.1,测试结果如下:

debug

调试构建: 在调试构建(debug build)任务中,moon 依旧表现优异,用时 2.3 秒,go 次之,为 2.9 秒,而 cargo 最慢,耗时 20.0 秒。

check

类型检查: 在类型检查(check)任务中,moon 的性能最佳,耗时仅为 1.4 秒,而 cargo 则需要 16.2 秒,go 在此项任务中无数据。go 没有类似于 moon checkcargo check 仅执行类型检查的命令,因此结果为 -

release

发布构建: 在发布构建(release build)任务中,moon 的表现依然出色,仅耗时 1.6 秒,go 为 3.1 秒,而 cargo 则未能完成任务。cargo build --release在该项目中耗尽内存后崩溃,因此结果为

值得注意的是,moon 的发布构建要比调试构建的速度更快。

而当 DR、DC、MR、MC 均为 8 时,共 4097 个模块,moon build 用时为 5.7 秒,go build 用时 11.2 秒,而 cargo build 用时 1043 秒。在此项测试中,moon 与 go 均在数秒内完成,而 cargo 已经无法在合理的时间完成此项目的构建。

标准库上的性能测试

目前 moonbitlang/core 为最大的 MoonBit 项目,截止到 2024/07/03,它共有 38177 行代码,46 个包,195 个.mbt文件,2576 个测试。对项目进行类型检查仅需 0.28 秒,执行全部测试仅需 1.27 秒。

马上使用 moon

  • 访问下载页安装脚本下载 MoonBit 工具链,或者在 VS Code 中安装 MoonBit 插件后根据提示一键安装

  • 用法:moon help

The build system and package manager for MoonBit.

Usage: moon [OPTIONS] <COMMAND>

Commands:
new Create a new moonbit package
build Build the current package
check Check the current package, but don't build object files
run Run WebAssembly module
test Test the current package
clean Clean the target directory
fmt Format moonbit source code
doc Generate documentation
info Generate public interface (`.mbti`) files for all packages in the module
add Add a dependency
remove Remove a dependency
install Install dependencies
tree Display the dependency tree
login Log in to your account
register Register an account at mooncakes.io
publish Publish the current package
update Update the package registry index
coverage Code coverage utilities
bench Generate build matrix for benchmarking (legacy feature)
upgrade Upgrade toolchains
version Print version info and exit
help Print this message or the help of the given subcommand(s)

Options:
--source-dir <SOURCE_DIR> The source code directory. Defaults to the current directory
--target-dir <TARGET_DIR> The target directory. Defaults to `source_dir/target`
-q, --quiet Suppress output
-v, --verbose Increase verbosity
--trace Trace the execution of the program
--dry-run Do not actually run the command
-h, --help Print help

如何贡献

我们欢迎来自社区各种形式的贡献,如文档、测试以及 issue 等,详细内容请参考贡献指南

MoonBit 开源计划

自去年正式发布以来,MoonBit 已于今年 3 月 8 日对外开放标准库,在社区的热情贡献下,已经成功实现完备的数据结构库,进一步丰富了 MoonBit 语言的应用场景,使语言生态系统走向成熟。在本次构建系统开源后,我们将于下个月 8 月 18 日发布 MoonBit Beta 预览版本,标志着 MoonBit 语言达到一个相对成熟的阶段,可以供早期用户和开发者进行实际项目的开发和测试。在今年年底 11 月 22 日,MoonBit 编译器核心部分将正式开源。

roadmap

你还能做什么

IDEA研究院编程语言MoonBit发布JavaScript后端,速度提升25倍

· 阅读需 15 分钟

从登顶HackerNews到走红日本社区,IDEA研究院MoonBit编程语言宣布重要更新:在率先通过支持WASM并显现显著优势后,将推出支持JavaScript后端的版本。

MoonBit诞生于2022年,是专为云计算及边缘计算设计的AI云原生编程语言及开发者平台。 作为一门诞生于AI与云原生的浪潮之中的编程语言,MoonBit汲取了多种语言的精华,特别是融合了Rust和Go的设计理念,使其不仅简洁易用,还具备强大的类型系统来增强容错性。同时MoonBit融合传统IDE智能与大模型智能,实现了智能代码生成的调整和修正。

MoonBit平台为WebAssembly提供了原生支持,在运行速度和代码体积方面领先于传统编程语言。随着团队能力的发展,MoonBit也扩展其支持到更多的后端,目前已新增对JavaScript的支持并带来前所未有的性能提升,在JS后端实现了超出Json5近8倍性能的优势。

为什么要支持JS:全球最大的开发者社区

根据GitHub发布的“编程语言”流行度排行榜【1】,JavaScript占据榜首位置,它是目前就业市场和应用开发领域中最受欢迎和流行的编程语言之一。JavaScript拥有全球最大的开发生态,因此,大多数编程语言都支持 JavaScript后端。MoonBit支持JavaScript,也意味着MoonBit进入了更加大众化开发者的生态,是在软件开发领域和应用场景全覆盖的一次重要演进。

Untitled

JavaScript常年高居编程语言流行度排行榜第一名。(数据来源:https://octoverse.github.com/2022/top-programming-languages

但在当前编程环境中,市面上的大部分alt-js language都不如原生性能。根据以往的基准测试,一些编译后的代码比原生JavaScript的体积大几乎350倍。尽管ReScript/ReasonML在编译到JavaScript上显示出一定优势(其开发者也是MoonBit平台的负责人),但其性能与原生JavaScript仍存在一定差距。因此,对于开发者而言,如果转译后的语言仅仅在可维护性上有少许提高,却以牺牲大量的体积和性能为代价,那么大多数情况下,原生JavaScript仍然是用户首选。

Untitled 1

数据来源:https://unsafeperform.io/blog/2022-07-02-a_small_benchmark_for_functional_languages_targeting_web_browsers/

为此,基于当前已有的alt-js language性能问题,MoonBit支持将代码编译到JavaScript后端,并在性能上显著超越原生JavaScript。

基准结果:生成代码执行速度超出 JS 近 25 倍

根据最新的JavaScript提案【2】— Iterator Helpers(迭代器助手),JavaScript能够原生支持无需中间数组的迭代操作。MoonBit也同步引入了相同的功能,进一步强化了在现代JavaScript开发环境中的适用性和效率。

我们先来看一个常见的例子:在处理大量学生成绩数据时,传统JavaScript代码通常需通过多个步骤处理数据,例如首先遍历1000个学生的成绩,将成绩转换为等级,然后按A、B、C、D、E排序,并筛选出A等级的学生,最后计算A等级学生的数量。这种方法虽直观,但由于需要生成多个中间数组,会导致性能问题。MoonBit在与原生支持迭代器助手的JavaScript进行比较后,编写出来的JavaScript代码显示,MoonBit的执行速度比使用原生迭代器助手快了25倍。

JS Iterator Helpers代码:

export function run(data) {
return data
.values()
.flatMap((c) => c.members)
.filter((it) => it.gender)
.map((it) => Math.min(100, it.score + 5))
.map((it) => grade(it))
.filter((it) => it === 'A')
.reduce((acc, _) => acc + 1, 0)
}

MoonBit代码

pub fn task(data: Array[ClassRecord]) -> Int {
data.as_iter()
.flat_map(fn { it => it.members.as_iter() })
.filter(fn { it => it.gender })
.map(fn { it => @math.minimum(100, it.score + 5) })
.map(fn { it => grade(it) })
.filter(fn { it => it == "A" })
.fold(fn { acc, _ => acc + 1 }, 0)
}

MoonBit引入了类似于Rust中的零成本抽象 —— Iter类型,这是一个针对常见数据处理场景进行特殊优化的库。此类型的设计利用了MoonBit的高级抽象能力,同时确保不引入任何额外的运行时开销。通过使用Iter,可以将多个处理步骤合并为一个连贯的流程,显著减少中间数组的生成,从而提高性能。MoonBit的实现使得开发者能够编写逻辑清晰且高效的代码,无需担心性能损耗。

这种实现方式优化了数据处理过程,尤其适合于需要处理大量数据或执行复杂数据转换的应用程序。使用Iter类型,开发者可以更方便地构建高效且易于维护的系统,充分发挥MoonBit的性能优势。

Untitled 2

Benchmark来源:https://github.com/moonbit-community/benchmark-202404.git

►**生成代码执行速度:**我们将MoonBit生成的代码的执行速度与JS进行对比,快了超过25倍。这种方法不仅简化了代码,还提升了处理效率,允许开发者专注于业务逻辑而非性能优化,减少开发者的心智负担。

►**一秒内执行测试任务的次数:**MoonBit的性能是原生JavaScript的25倍,这意味着MoonBit在每秒内能执行的操作(Ops/Sec)远超原生JavaScript。

Untitled 3

以下是使用MoonBit进行优化后的代码示例及其编译结果:

image

Iter代码MoonBit生成的JS

MoonBit JS vs. JSON5:性能快近8倍

最新的性能测试结果显示,MoonBit Core标准库移植了npm上top 0.1%的Json5库,经过MoonBit编译之后的JS,比Json5库快接近8倍。**计算任务执行效率的性能提升意味着 MoonBit能够在相同的时间内执行更多的解析任务,**极大地提高了数据处理效率。

Untitled 4

Benchmark 来源:https://github.com/moonbit-community/benchmark-202404.git

为什么MoonBit比Json5的运行速度快?

之所以能取得这样的性能优势,关键在于MoonBit在处理数据时做了优化:将字符串直接转换为整数进行处理。 由于JavaScript本身没有字符类型,字符在JS中也是以字符串形式存在的,这意味着任何字符操作,实际上都是在字符串层面上进行,自然比直接操作整数要慢。

此外,MoonBit利用编译技术优化了代码处理过程。 例如,在编译过程中,字符常量会被转换为它们的Unicode codepoint值。这样在执行时就可以直接利用这些预编译的整数值,而不是在运行时动态处理字符。同样,对于switch语句,整数的switch操作可以编译成更快的指令,而字符串的switch操作效率就相当于一连串的if判断,速度会慢很多。MoonBit还引入了多项与JS无关的通用优化技术,如常量折叠、尾递归转换为循环、可选字符(Option[Char])的解包优化以及迭代器融合(Iter fusion),从而提高了整体编译和运行效率。

除了这些已经实现的优化,MoonBit团队仍在不断探索新的改进方式。例如,未来的优化计划包括将整型数组(Array[Int])编译到Int32Array,等等。为了确保性能的持续提升并防止性能回退,MoonBit已建立了一套监测机制,对新的更改进行严格的性能评估。

除了运行性能,MoonBit在编译性能上也表现出色,能够无需等待响应,即时编译至JS。MoonBit JS复用MoonBit的IDE以及整套工具,借鉴以往IDE的架构经验。MoonBit在语言设计之初充分考虑了IDE、构建系统、语言设计的协同,实现类型检查和推断的高度并行化和增量化。

开箱即用的工具链支持

此前有很多语言长时间内都未能支持sourcemap调试功能。MoonBit不仅支持JavaScript代码,且目前所有的工具链都支持开箱即用

►完善的sourcemap支持,友好的调试支持

去年,MoonBit推出了调试器(Debugger)功能,这是相对于其他语言通常在成熟阶段才拥有的。即便跟编译性能与运行性能相近的ReScript相比,MoonBit已提供sourcemap支持,而ReScript在这方面迟迟没有提供支持。目前,该功能已支持源码映射、基于源码设置断点、输出sourcemap等,在浏览器中进行源码调试。这项功能的推出不仅减轻了开发者在代码调试方面的负担,还显著提升了开发和调试的流畅性。

MoonBit的sourcemap和调试支持优化开发者的调试体验,确保JavaScript代码的生成尽可能贴近源码,变量名和字段名保持一致,便于使用浏览器的DevTools进行源码检查和调试。

4

Repo地址:https://github.com/moonbit-community/moonbit-js-debug-demo.git

►本地 test codelens 的全新支持

此外,MoonBit IDE支持本地环境的test codelens,且本地环境已支持expect test的自动更新。相较于此前用户需要手动编写命令行的版本,现在只需要点击「Run Test | Update Test」即可完成毫秒级的编译,几乎无需等待响应,大幅提升了测试的效率。

5

功能的具体效果展示

如果你想自己尝试一下,可以复制点击: https://github.com/moonbit-community/benchmark-202404/tree/master/bench_json5

MoonBit 初获国际认可

MoonBit在2023年8月18日首发后,在国内外社区引发了热烈的讨论,并在发布不到一天的时间内登顶硅谷知名科技社区HackerNews头版头条。

Untitled 5

MoonBit登顶HackerNews头版头条

随着开源计划的逐步推进,MoonBit在今年3月首次开放标准库后,迅速获得了社区的广泛认可和积极响应。标准库开源至今,MoonBit收到了超过501次添加新功能的记录,并且收到261次请求将改动合并到主代码库中。其中80%的提交已被成功合并,这些贡献来自于40多位代码提交者,不仅包括具有丰富开发经验的专业人士,也有国内外的高中在读学生。值得一提的是,这些学生提交的代码质量堪比资深开发者。

Untitled 6

MoonBit标准库代码贡献

此外,今年4月,技术爱好者开发者@mizchi在日本著名的开发者社区zenn.dev上发表了一篇关于MoonBit的详细介绍文章,**该文章引发热议并迅速攀升至热门榜首。**文章通过MoonBit与其他编程语言的对比和实际体验,进一步加深社区对MoonBit优势的认识,使得该平台获得了新一轮的正面评价和关注。

Untitled 7

日本开发者社区对MoonBit的评价(点击大图)

未来展望

截至今日,MoonBit已经通过支持WebAssembly和JavaScript来完善其生态系统,并在行业内达到领先水平。在未来,MoonBit计划扩展到更多后端平台,如Native和JVM,并开发自有的运行时及云端部署环境,进一步强化其工具链支持。MoonBit平台不仅仅是一种编程语言,它还提供了一个全面的开发工具链,包括集成开发环境(IDE)、编译器、构建系统和包管理器等,为开发者提供了一站式的软件开发解决方案。

Untitled 8

MoonBit本年计划“剧透”

附录:

【1】:https://octoverse.github.com/2022/top-programming-languages

【2】:https://github.com/tc39/proposal-iterator-helpers

推荐一个超好用的测试工具,值得体验!

· 阅读需 11 分钟

在软件开发领域中,测试是确保质量与可靠性的必要环节。俗话说得好“工欲善其事,必先利其器”,测试工具越简单、用户友好度越高,开发者编写测试的意愿度就越高。

为了满足大家的测试需求,MoonBit 标准库最近引入了 inspect 函数,我们也称之为 expect 测试,它可以帮助程序员快速编写测试。

相比于 OCaml 和 Rust,MoonBit 提供了更加简洁高效的测试体验。我们的测试工具不仅操作简便,而且无需任何外部依赖,支持开箱即用,简化了测试流程。

这里简单介绍一下 MoonBit

MoonBit 是国内首个工业级编程语言及其配套工具链(https://www.moonbitlang.cn/)是由粤港澳大湾区数字经济研究院(福田)基础软件中心打造的AI原生的编程语言以及开发者平台。MoonBit 自2022年10月推出以来,通过创新框架在程序语言界形成后发优势,已在编译速度、运行速度和程序体积上取得了显著的优势。MoonBit 平台的出现不仅仅作为一种编程语言,更提供一个完整的开发工具链,包括 IDE、编译器、构建系统、包管理器等。

接下来,让我们进一步了解 inspect 函数的使用。

忽略掉与位置相关的参数后,inspect 函数签名为:

pub fn inspect(obj : Show, ~content: String = "")

这里 obj 是任意一个实现 Show 接口的对象,~content 是一个可选参数,表示我们所期望的 obj 转化为字符串后的内容。听起来有点绕?让我们先来看看 inspect 的基本用法:

01 基本用法

首先,让我们使用 moon new hello 创建一个新项目

此时项目的目录结构如下:

.
├── README.md
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── main
│ ├── main.mbt
│ └── moon.pkg.json
├── moon.mod.json

打开 lib/hello_test.mbt,将内容替换为:

fn matrix(c: Char, n: Int) -> String {
let mut m = ""
for i = 0; i < n; i = i + 1 {
for j = 0; j < n; j = j + 1 {
m = m + c.to_string()
}
m += "\n"
}
m
}

这里,matrix 函数接受一个字符 c 和整数 n 作为参数,生成一个 n * n 大小的字符矩阵。

接下来,添加如下内容:

test {
inspect(matrix('🤣', 3))?
}

打开终端,执行 moon test 命令可以观察到类似如下输出:

Diff:
----
🤣🤣🤣
🤣🤣🤣
🤣🤣🤣

----

这里的输出展示了 matrix 函数的实际输出和 ~content 参数的差异,执行 moon test -u 或者 moon test --update 可以观察到 lib/hello_test.mbt文件中的测试块被自动更新成:

test {
inspect(matrix('🤣', 3), ~content=
#|🤣🤣🤣
#|🤣🤣🤣
#|🤣🤣🤣
#|
)?
}

让我们再把 n 改成 4,然后执行 moon test -u 可以观察到测试块被自动更新成:

test {
inspect(matrix('🤣', 4), ~content=
#|🤣🤣🤣🤣
#|🤣🤣🤣🤣
#|🤣🤣🤣🤣
#|🤣🤣🤣🤣
#|
)?
}

1.GIF

02 稍微复杂的例子

一般来说,写完一个函数后,我们需要为其编写一些单元测试。最简单的是断言测试,MoonBit 标准库中提供了 @assertion.assert_eq 函数,用于判断两个值是否相等。对于上述例子来说测试编写相对容易,因为我们可以轻易预测输出结果是什么。

接下来让我们看一个稍微复杂一点的例子:如何测试计算斐波那契数列第 n 项的函数。

首先在 lib 目录下新建一个 fib.mbt 的文件,然后粘贴如下内容:

fn fib(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib(n - 1) + fib(n - 2)
}
}

我们需要一些测试来验证我们的实现是否正确。如果用断言测试,那么编写测试的流程是什么呢?假设我们想测试输入为 10 时 fib的结果,我们会写下类似如下的代码:

test {
@assertion.assert_eq(fib(10), ???)?
}

当我们写下这个测试的时候,会立即遇到一个问题,我们并不知道 assert_eq 中右侧的期望值应该是什么。我们可以自己在纸上计算,或者找一份斐波那契数列参考列表、或者执行我们自己实现的 fib 函数。总之,不论用什么方法,我们需要得到 fib(10) 的期望值是 55,才能完成一个测试用例的编写。

此时 lib/fib.mbt 文件内容应为:

fn fib(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib(n - 1) + fib(n - 2)
}
}

test {
@assertion.assert_eq(fib(10), 55)?
}

然后执行 moon test 可以观察到如下输出:

Total tests: 2, passed: 2, failed: 0.

大家可以感受到,这里的反馈周期是很长的。通常来说,用这种方法编写测试并不是很愉悦。

对于 fib 这个例子我们可以相对容易找到一个正确的值作为参考。然而,大多数情况下,我们想测试的函数并没有其他“真值表”作为参考。我们需要做的是给这个函数提供输入,然后观察其输出是否符合我们的期望。这种模式比较常见,但其他工具链很少提供支持。因此,我们的 MoonBit 工具链提供了对这一测试模式的一等支持。通过使用 inspect 函数,我们只需要提供输入,而不需要提供期望值。

接下来,让我们用 inspect 函数编写输入分别为 8,9,10 时的测试用例,此时 lib/fib.mbt 文件的内容如下:

fn fib(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib(n - 1) + fib(n - 2)
}
}

test {
@assertion.assert_eq(fib(10), 55)?
}

test {
inspect(fib(8))?
}

test {
inspect(fib(9))?
}

test {
inspect(fib(10))?
}

通过执行:

moon test

可以观察到实际输出与 inspect 函数中 ~content的差异:

$ moon test
test username/hello/lib/fib.mbt::0 failed
expect test failed at path/to/lib/fib.mbt:10:3-10:18
Diff:
----
21
----

test username/hello/lib/fib.mbt::1 failed
expect test failed at path/to/lib/fib.mbt:14:3-14:18
Diff:
----
34
----

test username/hello/lib/fib.mbt::2 failed
expect test failed at path/to/lib/fib.mbt:18:3-18:19
Diff:
----
55
----

接下来,我们的工作变成了检查这个输出是否正确,如果确信这些输出是正确的,通过执行 moon test -u,在 lib/fib.mbt 文件中对应的测试块会被自动更新成:

test {
inspect(fib(8), ~content="21")?
}

test {
inspect(fib(9), ~content="34")?
}

test {
inspect(fib(10), ~content="55")?
}

2.gif

通过这种编写测试然后立即获取反馈的模式,能够极大提升编写测试的愉悦感。

接下来,让我们来看一个修改函数行为导致输出变化的例子。

例如,我们现在想让 fib 的第一项从 1 而不是从 0 开始,首先我们将 fib 函数中的 0 => 0 修改为 0 => 1

fn fib(n : Int) -> Int {
match n {

然后执行 moon test 可以看到 expect test 自动为我们展示了前后差异:

$ moon test
test username/hello/lib/fib.mbt::0 failed: FAILED:path/to/lib/fib.mbt:10:3-10:36 `89 == 55`
test username/hello/lib/fib.mbt::1 failed
expect test failed at path/to/lib/fib.mbt:14:3-14:33
Diff:
----
2134
----

test username/hello/lib/fib.mbt::2 failed
expect test failed at path/to/lib/fib.mbt:18:3-18:33
Diff:
----
3455
----

test username/hello/lib/fib.mbt::3 failed
expect test failed at path/to/lib/fib.mbt:22:3-22:34
Diff:
----
5589
----

Total tests: 5, passed: 1, failed: 4.

3.gif

这里的输出结果发生了位移,符合期望,我们大概率可以确定输出是对的。于是通过执行 moon test -u 自动更新测试结果。

等等!为什么自动更新后,还有一个测试用例失败了呢?

Total tests: 5, passed: 4, failed: 1.

这是因为我们忘记了修改断言测试,与 expect 测试不同,断言测试并不会自动更新。我们需要手动将断言测试对应的测试块修改为:

test {
@assertion.assert_eq(fib(10), 89)?
}

从这个例子也能看出来,expect 测试是可以与断言测试协同工作的。

重新执行 moon test,可以看到全部测试都通过了。

Total tests: 5, passed: 5, failed: 0.

想象一下,如果我们之前有数百个断言测试,修改起来将会非常麻烦。通过使用 expect 测试,可以让我们从枯燥的更新工作中解脱出来。

以上,我们通过两个例子展示了它们如何通过编写测试并获得即时反馈,显著提升测试效率的能力。

我们鼓励你尝试使用 MoonBit 的 expect 测试,体验其在实际应用中的便利和高效。

推荐阅读:

https://blog.janestreet.com/the-joy-of-expect-tests/

你还可以了解更多内容:

【新功能上线】mooncakes.io:新一代的MoonBit包管理平台来啦!!

· 阅读需 7 分钟

Cover

今天,我们很高兴地宣布:mooncakes.io (MoonBit的包管理和共享平台)正式上线!与此同时,我们还推出了Moondoc,它用于记录mooncakes.io内各种包和库,确保开发者可以轻松使用各种资源。

MoonBit最大的价值在于生态,更多的人更早的参与进来可以帮助我们一起完善MoonBit平台,一起分享它的成长。虽然还处于非常早期的阶段,还有很多地方需要完善,但是我们想第一时间分享给大家!

接下来,让我们一起深入了解 mooncakes.io 的独特之处。