跳到主要内容

5 篇博文 含有标签「MoonBit」

查看所有标签

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 的独特之处。