差不多工作了 5 年,现在对 JS/TS 语言构造愈发感到不快了,所以最近都在学点其他的语言调剂一下,不然真的要废内功了。
近期看了下由张宏波团队开发的国产编程语言 MoonBit,一眼就爱上了,是 OCaml like 的语言,有股 rust 味,但是没有 rust 生命周期那些烧脑的特性,语言自带 GC。
本文介绍一下我感觉很好的地方 (rust with gc 笑)
# 非常好安装体验 ↵
查看官网 https://www.moonbitlang.cn/download/ 有详细说明,基本是傻瓜式操作,而且由于是国产的下载速度非常快,我估计不会有人卡在这一步的,在安装完之后就可以用
moon
命令来使用相关工具链了,比如这样新建项目进行开发:00$ moon new myfirst # 创建一个叫做 myfirst 的项目 01$ cd myfirst # chdir 到项目里 02$ moon run main # 编译运行
# 一等公民函数 ↵
在 moonbit 里函数是一等公民,使用词法作用域,因此会有跟 js 一样的闭包效果:
00// 写在最外面的叫做「顶层函数」 01fn counter() -> () -> Int { 02 let mut i = 0 // 可变值需要带上 mut 标记 03 04 // 嵌套在内的叫做「局部函数」 05 // 局部函数的签名可以比较简略,编译器会自动推导 06 fn inc() { 07 // 函数走的是跟 js 一样的词法作用域 08 let r = i 09 i = i + 1 10 return r 11 } 12 13 return inc 14} 15 16fn main { 17 // 因此也会有一模一样的闭包效果 18 let count = counter() 19 println(count()) // 0 20 println(count()) // 1 21}
# 带标签的函数参数 ↵
支持标签参数、可选参数等高级特性:
00// 此处的内置类型 Ref 可以理解为 ts: type Ref<T> = { val: T } 01// 1. 用 ~counter 的方式来给参数加标签 02// 2. 用 = 来给参数带一个默认值使其变成可选参数 03// 3. 默认值每次执行都会给一个「新的」 04fn incr(~counter : Ref[Int] = { val: 0 }) -> Ref[Int] { 05 counter.val = counter.val + 1 06 counter 07} 08 09fn main { 10 println(incr()) // 1 11 println(incr()) // 依然是 1,因为重新求值了默认表达式,产生了一个新的 Ref 12 let counter : Ref[Int] = { val: 0 } 13 println(incr(~counter)) // 1 14 println(incr(~counter)) // 2,因为两次调用使用了同一个 Ref 15}
参数默认值每次执行都会执行新的,不会复用,如果想一直复用同一个默认值,那么可以将其提升到全局
00let default_counter : Ref[Int] = { val: 0 } 01 02fn incr(~conuter : Ref[Int] = default_counter) -> Int { 03 counter.val = counter.val + 1 04 counter.val 05} 06 07fn main { 08 println(incr()) // 1 09 println(incr()) // 2 10}
# 函数式循环 ↵
看了真过瘾,专门给 ADT 设计的 reduce 循环:
00fn sum(xs: List[Int]) -> Int { 01 loop xs, 0 { 02 Nil, acc => break acc // break 可以省略 03 Cons(x, rest), acc => continue rest, x + acc 04 } 05} 06 07fn init { 08 println(sum(Cons(1, Cons(2, Cons(3, Nil))))) // 6 09}
# 多行字符串 ↵
字符串底下用 UTF-16 进行编码,除了常用的字符串优秀实践外,还提供了多行字符串(js 的模板字符串在多行的情况下会把缩进也一起编进去,实际使用很蛋疼)
00fn main { 01 let str = 02 #| Hello 03 #| World 04 println(str) 05}
# enum adt ↵
爷最爱的 adt
00enum DragState { 01 Invalid 02 Active(Int, Int) 03} 04 05fn printDragState(s: DragState) -> Unit { 06 match s { 07 Invalid => println("未激活拖拽") 08 Active(dx, dy) => { 09 println("拖拽中 ... dx=(dx) dy=(dy)") 10 } 11 } 12} 13 14fn init { 15 printDragState(Invalid) // 未激活拖拽 16 printDragState(Active(1, 2)) // 拖拽中 ... dx=1 dy=2 17}
# 没有空值 ↵
由于提供了基于 ADT 构造的 Option 和 Result, moonbit 没有额外提供 null 这些空值了,配合
?.
问号操作符能写出更健壮的代码:00fn may_fail() -> Option[Int] { ... } 01 02fn f() -> Option[Int] { 03 let x = may_fail()? 04 let y = may_fail()?.lsr(1) + 1 05 if y == 0 { return None } 06 Some(x / y) 07} 08 09fn may_error() -> Result[Int, String] { ... } 10 11fn g() -> Result[Int, String] { 12 let x = may_error()? 13 let y = may_error()? * 2 14 if y == 0 { return Err("divide by zero") } 15 Ok(x / y) 16}
# moonckes 包管理 ↵
moonbit 提供了 mooncakes.io 作为包管理平台,跟语言深度结合,比 npm 高到不知道哪里去, 下面是我发的一个测试包,自带文档工具
# EOF ↵
这门语言是新开发的,目前仍在快速迭代中,从我去年第一次关注以来已经多了非常多功能了,前两天还发布了新的 js 后端 —— 也就是可以编译成 js 使用,试了下非常好,可以充分享受写高级语言的乐趣。