这两天正式接触了一下 MoonBit,尝试将一个大约 1000 行的 C 语言编写的 JS Parser 转译成 MoonBit 语言。 聊一下我的客观感受:
- 前期最大的困境其实是文档的完备性。我不仅阅读了官方文档,还研究了官方 GitHub 上的
core
包源码,但仍然觉得其中有很多细节没有完整地整理出来。- 但总体来说,MoonBit 给我的感觉像是 Rust 的一个更“干净”的版本。
- 语法挺“干净”的。之所以会这样说,是因为在开始接触之前,我断断续续关注官方发布的新闻或博文,当初曾疑惑这门语言为何一直在增加语法糖。但实际使用起来发现并不繁琐,反而觉得这些语法糖设计得恰到好处,且整体规则清晰。
- MoonBit 号称是“AI 友好的编程语言”,但在官方的(VS Code)插件中,我看到的内置 AI 提示词内容还比较基础(大约 500 行主要用于介绍基本语法)。相比之下,直接将官方 Markdown 文档喂给大型语言模型可能更有效率。在官方 AI 提示词的基础上尝试修复我遇到的一些问题时,往往只能解决表面现象,更深层次的转换仍需依赖 AI 模型自身的能力,而非这部分预设的提示词。
- 最大的差异在于“模式匹配”的运用。C 语言编写的 JS Parser 通常采用移动指针、逐个字符判断的风格,这与 MoonBit 所推崇的模式匹配风格有所不同。虽然 MoonBit 也能像 C 语言那样进行字符级解析,但要发挥编译器的优势,还是应该多使用模式匹配。目前,AI 在将这种指令式代码自动翻译成更偏声明式的模式匹配风格方面表现不佳,因此多数情况下,这种转换仍需要人工进行。
- LSP 的速度和稳定性没有达到我的预期。当使用 AI 生成代码并进行快速修改时,LSP 服务偶尔会卡死(例如,VS Code 插件长时间无响应)。对于我尝试的这个千行级别的项目,LSP 的分析速度也未如想象中迅速,或许是项目体量还不够大,未能充分体现其性能优势。
- 另外,一些错误代码(错误信息)的提示还是比较含糊。比如我使用
enum
去assert_eq
做比较,居然报错说我不能实例化一个 readonly 对象,有点莫名其妙,我改成assert_true
+match
语法才绕过去。 - 以及我遇到的一个比较棘手的情况是,
match
表达式中如果存在深层嵌套的语法错误(例如,某个分支内的if
表达式缺少了必要的括号),错误信息可能会指向整个match
块,使得难以快速定位到具体的细微错误。这种情况对于初学者来说定位问题会非常困难。 - 错误信息含糊也影响了 AI 辅助修复代码的效率。当我尝试让 AI 进行修复时,它提供的建议往往帮助不大,甚至浪费了调试时间。
- 不过总体感知上,LSP 的主要功能稳定性尚可,至少没有遇到非常离谱的错误反馈。我认为当前阶段,持续积累并优化对更多种类异常情况的精确提示,对 MoonBit 工具链而言至关重要。
- 另外,一些错误代码(错误信息)的提示还是比较含糊。比如我使用
- 我现在开始觉得官方支持原生编译 (Native) 是一个非常正确的决定。MoonBit 的一些优势,如“干净”的工具链和依赖管理设计,与 Rust 有相似之处。目前 Rust 在很多场景下被用于开发工具链,而纯粹的 WASM 作为最终交付产物的需求场景相对特定。因此,当 MoonBit 支持原生编译后,一些简单的工具链项目也就可以用 MoonBit 开发。虽然 WASM 是一个强大的目标平台,但原生编译通常能带来更好的性能和更低的资源开销,这对于工具链等性能敏感的应用非常重要。
- MoonBit 的发展仍将是一个漫长的过程。在这次试用过程中,我暂时还没有发现一个场景是非 MoonBit 不可的,生态系统的成熟度是主要因素之一。
- 我其实非常期待 MoonBit 能够对前端开发,特别是 React 生态,带来一些新的可能性。JavaScript/TypeScript 的语法在某些方面(例如 JSX 的实现、编译期优化)受到语言本身的限制,而 MoonBit 在这方面有其优势,或许可以为类似 React 的框架提供专门的语法糖。另外,MoonBit 的语法设计和静态类型系统可能比 JS/TS 更适合实现类似 React Compiler 的编译期优化。
- 例如,Dioxus 基于 Rust 的构建系统和宏能力实现了
rsx!
宏语法,并构建了一整套生态。而我认为 MoonBit 因为自带 GC,对业务开发者可能比 Rust 更友好,在某些方面或许更适合前端或全栈业务开发。 - 我注意到 Lit 生态中的
@lit-labs/compiler
也在进行编译期优化的类似尝试。 - 但 Lit 毕竟是基于 JS/TS 这类动态性较强的语言,这使得它在进行充分的静态分析并据此进行安全且深度的 AST 重写方面,可能不如 Rust 或 MoonBit 这样具有强静态类型系统的语言有优势。React Compiler 也在朝这个方向努力,但它需要有回退机制,即对无法安全优化的部分选择不优化。
- 例如,Dioxus 基于 Rust 的构建系统和宏能力实现了
- MoonBit 社区的 rabbit-tea 项目是一个不错的开端。也许将它与 Rust 的 GUI 库 Iced 对接会是一个有趣的方向。我不确定其可行性,毕竟这涉及到 Rust 与 MoonBit 之间的互操作。一种可能的方案是通过 WASM FFI:将 Iced 编译成 WASM 模块,然后由 MoonBit 通过 FFI 调用,但这需要对其性能和可行性进行验证。
- 但坦率地说,如果直接用 MoonBit 现有的语法进行声明式 GUI 开发,代码可能会显得有些啰嗦。如果没有针对性的语法糖或 DSL,代码格式化后的可读性也可能不高。 我大胆猜测,按照 MoonBit 团队以往的设计风格,未来可能会引入类似这样的解决方案:
>| div { >| h1 { >| text("Title:\{model.to_string()}") >| } >| button(click=Msg::Increment) { text("+") } >| button(click=Msg::Decrement) { text("-") } >| }
>|
的设计可能借鉴了多行字符串#|
,这或许能让解析更简单,对 AI 生成代码也更友好。- 以
>|
开头可以明确告知编译器进入一种特定的 DSL 或语法糖模式。 - 这借鉴了 Kotlin 构建器 (Builder) 的一些语法思想,例如:
div {}
可能等价于div(fn(){})
button(click=Msg::Decrement) {}
可能等价于button(click=Msg::Decrement, fn(){ ... })