跳到主要内容

weekly 2024-02-26

· 阅读需 4 分钟

MoonBit 更新

1. 支持云原生调试功能

现在,你可以通过访问 try.moonbitlang.cn,直接在浏览器中使用 devtools 调试 MoonBit 程序,无需安装任何软件。具体的使用步骤如下:

2. MoonBit 支持使用 for 关键字定义的函数式循环控制流

MoonBit 现在支持使用 for 关键字定义的函数式循环控制流,其性能接近于 C/C++ 等底层语言,比如 fib 函数可以写成如下形式:

fn fib( n : Int ) -> Int {
for i = 0, a = 1, b = 2
i < n
i = i + 1, a = b, b = a + b {
} else { b }
}

MoonBit 的 for 循环可以作为表达式返回一个值,比如上述程序中在循环结束后使用 b 作为整个 for 循环的值,也可以在 for 的循环体中通过 break 提前返回,比如:

fn exists(haystack: Array[Int], needle: Int) -> Bool {
for i = 0; i < haystack.length(); i = i + 1 {
if haystack[i] == needle {
break true
}
} else {
false
}
}

此外,在 for 循环中可以像传统语言一样使用 continue 进入下一次循环,MoonBit 额外提供了带参数的 continue 来指定下一次循环过程中循环变量的值,比如:

fn find_in_sorted[T](xs: Array[(Int, T)], i: Int) -> Option[T] {
for l = 0, r = xs.length() - 1; l < r; {
let mid = (l + r) / 2
let k = xs[mid].0
if k == i {
break Some(xs[mid].1)
} else if k > i {
continue l, mid
} else {
continue mid + 1, r
}
} else {
None
}
}

在不需要返回值的情况下,else 分支可以省略,比如:

fn print_from_0_to(n: Int) {
for i = 0; i <= n; i = i + 1 {
println(i)
}
}

3. Inline test 改进

测试的返回类型从Unit改成了Result[Unit,String],用于表示测试的结果:

 test "id" {
if (id(10) != 10) { return Err("The implementation of `id` is incorrect.") }
}

编译器会自动将test "id" {...} 的语句块{...}使用Ok()包裹起来。因此,当语句块的类型为Unit并且没有提前return时,表示inline test测试通过。配合问号操作符,可以让测试变得更加优雅:

fn id(x : Int) -> Int {
x + 1 // incorrect result
}

fn assert(x : Bool, failReason : String) -> Result[Unit,String] {
if x { Ok(()) } else { Err(failReason) }
}

test "id" {
assert(id(10) == 10, "The implementation of `id` is incorrect.")?
}

执行moon test,输出如下:

➜  my-project moon test
running 1 tests in package username/hello/lib
test username/hello/lib::hello ... ok

test result: 1 passed; 0 failed

running 1 tests in package username/hello/main
test username/hello/main::id ... FAILED: The implementation of `id` is incorrect.

test result: 0 passed; 1 failed

Hello, world!

4. 改进 VS Code 插件的函数签名提示,现在会显示参数名:

5. 改进了 VS Code 插件对 core 包开发的支持

6. moon new 支持快速创建新项目

  • moon new hello 在文件夹 hello 中创建一个名为 username/hello 的可执行项目
  • moon new hello --lib 在文件夹 hello 中创建一个名为 username/hello 的模块

weekly 2024-02-19

· 阅读需 3 分钟

MoonBit更新

1. 增加了functional for loop控制流支持

与传统的命令式for loop 不同,循环变量是不可变的。这样的设计将来也容易抽取出来做形式化验证:

fn init {
for i = 0; i < 5; i = i + 1 {
debug(i)
// i = i + 4 error: The variable i is not mutable.
}
}

输出:

01234

functional for loop也支持多个绑定。与其他语言不同的是,x和y在functional for loop的第三个表达式里具有同时更新的语义:

fn init {
for x = 0, y = 0; x < 10; x = x + 1, y = x + 1 {
// ^~~ 这里x的值是更新前的
println("x: \(x), y: \(y)")
}
}

输出:

x: 0, y: 0
x: 1, y: 1
x: 2, y: 2
x: 3, y: 3
x: 4, y: 4
x: 5, y: 5
x: 6, y: 6
x: 7, y: 7
x: 8, y: 8
x: 9, y: 9

functional for loop内部也支持使用breakcontinue

fn init {
let xs = [0,1,2,3,4,5,6,7,8]
let mut sum = 0
for i = 0, v = xs[0]; i < xs.length(); i = i + 1, v = xs[i + 1] {
if v % 2 == 0 { continue }
if v >= 7 { break }
sum = sum + v
}
debug(sum) //output: 9
}

2. 改进moon new创建项目的向导

现在可以用方向键选择创建lib或者exec项目:

3. IDE支持管道运算符的智能补全

对于第一个参数类型与管道左侧表达式类型相等的函数,会放在补全列表的顶部,其它的补全选项仍然会显示在列表后。

4. 根据社区反馈调整了pipe表达式

现在管道运算符右侧支持Double::to_int这样的函数调用。

fn init {
debug(3.14 |> Double::to_int) // output: 3
debug(4 |> Array::make('c')) // output: ['c', 'c', 'c', 'c']
}

5. 修复IDE中缀表达式错误地插入inlay hint的问题

weekly 2024-02-05

· 阅读需 2 分钟

MoonBit 更新

1. 新增多行字符串支持

每行需要以#|开头。多行字符串每行之间允许断开、插入注释,字符串内不支持转义和字符串插值。

2. 新增函数式的loop循环

其中continue 只允许出现在尾递归调用的位置,loop内允许使用break提前返回一个值。

3. 提供Trait::method调用支持

支持以Debug::debug_write(self, buf)的形式调用trait method

4. 添加实验性标准库机制

最新的安装脚本会将标准库下载到 ~/.moon/lib/core 目录下。目前标准库的内容以及相关IDE支持暂不稳定,因此构建系统默认不链接标准库。对于想要提前体验标准库功能的开发者们,可以在 moon check|build|run|test 命令后添加 --std 选项用于链接标准库到当前项目。

5. 支持隐式到trait object的转换

在上下文中明确需要一个trait object时,会自动插入 as SomeTrait。例如下面的代码:

现在我们可以省略as Debug

6. 支持函数参数的inlay hint

7. 字符串和char字面量支持unicode转义、十六进制转义和八进制转义

weekly 2024-01-29

· 阅读需 2 分钟

MoonBit更新

1. 新增特性Trait object:

能够显式地将类型不同但实现相同trait的值装箱并表示成同一个类型,实现函数的动态分发。

fn get_show_list() -> List[Show] {
let a = 42 as Show
let b = "xxx" as Show
let c = 3.14 as Show
List::Cons(a, Cons(b, Cons(c, Nil)))
}

fn init {
fn print_show_list {
List::Cons(a, rest) => { println(a); print_show_list(rest) }
List::Nil => ()
}
print_show_list(get_show_list())
}

2. 新增管道运算符

提供类似于链式调用的语法,可以串联多个连续的函数调用,省去let name = ...的代码。例如value |> func1(arg1,arg2) |> func2 相当于:

let a = value
let b = func1(a, arg1, arg2)
func2(b)

另一个例子:

fn sub2(x : Int, y : Int) -> Int {
x - y
}

fn sum3(x : Int, y : Int, z : Int) -> Int {
x + y + z
}

fn init {
6 |> sub2(5) |> sum3(1,2) |> println()
}

3. 字符串支持使用\xFF进行十六进制转义

fn init {
let data = "\x48\x65\x6c\x6c\x6f"
println(data) //output: Hello
}

4. Inline test变更

现在test mode也会执行fn init,执行顺序在inline test之前。 |690x498

5. Moonfmt:改进类型和长数组字面量的缩进

原代码:

改进前的格式化效果:

改进后的格式化效果:

weekly 2024-01-22

· 阅读需 3 分钟

MoonBit更新

1. 新增矩阵函数的语法糖

新增矩阵函数的语法糖,用于方便地定义局部函数和具有模式匹配的匿名函数:

fn init {
fn boolean_or { // 带有模式匹配的局部函数
true, _ => true
_, true => true
_, _ => false
}
fn apply(f, x) {
f(x)
}
let _ = apply(fn { x => x + 1 }, 42) // 匿名函数
let _ = apply(fn { // 带有模式匹配的匿名函数
0 => 0
1 => 1
_ => 2
}, 42)
}

2. 新增使用 T::{ ... }构造结构体的语法

这个新语法可用于显式的指定结构体的类型,并会使得结构体内有更好的补全:

struct T {
x: Int
y: Int
}

struct T2 {
x: Int
y: Int
}

fn init {
let x = T::{x: 1, y: 2}
debug(x.x + x.y) // 3
}

3. 正式移除 var id = expr 的语法

4. 增加了新的关键词 test

新的测试语法 test "name" {},用于代替原先的fn test_name(){}。目前暂时只有顶格缩进的test会被识别成关键字,未来将不再支持使用test作为标识符。 旧语法: fn test_ name {} 新语法: test " name " {}

5. 支持在 init 或者 test 代码块中使用 return 语句

fn init  {
if i > 0 {
return
}
...
}

test {
if i > 0 {
return
}
...
}

插件更新

改进了语法高亮:

Before:

After:

mooncakes.io 更新

1. 新增 mooncakes.io 注册方式

现在mooncakes.io 支持使用用户名+邮箱方式注册,而不仅限于之前的GitHub登录方式。现在新用户可以抢先注册你心仪的用户名。(注意:用户名字符需要>=5,如果小于5需要联系管理员后台操作。)

$ moon register
Choose a way to register: Email
You chose: Email
Username: xxxxxx
Password: [hidden]
Email: xxxxxx@xxx.xx
Send verification code to your email[bzy_sustech@foxmail.com]? yes
Please input the verification code: xxxxxx
Register successfully!
API token saved in ~/.moon/credentials.json

2. 增加跳转到源代码的功能

weekly 2024-01-15

· 阅读需 3 分钟

MoonBit更新

1. 放宽了match的右手侧的语法,允许单个语句的出现。现在允许下面的写法:

match x {
A => return
B => return 1
C => i = i + 1
D => break
E => continue
F => while true {}
}

2. 修复formatter的各种bug,例如:

源代码 修复前 修复后
fn init {
let mut a = 1
{
{
let a = 2
f(a)
}
let a = 3
f(a)
{
let a = 4
f(a)
}
}
f(a)
}
fn init {
let mut a = 1
let a = 2
f(a)
let a = 3
f(a)
let a = 4
f(a)
f(a)
}
fn init {
let mut a = 1
{
{
let a = 2
f(a)
}
let a = 3
f(a)
let a = 4
f(a)
}
f(a)
}

3. 新增实验性inline测试机制

声明格式为fn test_*,inline测试需要在普通的 *.mbt 文件中(而不是 *_test.mbt)声明,它既不能有参数也不能有返回类型,例如以下写法会报错:

现在 moon test 除了会执行每个包中以 _test.mbt 结尾的测试文件,还会执行每个包中的 inline 测试。

构建系统更新

1. moon new给各个选项增加了默认值,用户可以使用回车选择默认值

$ moon new
Enter the path to create the project (. for current directory) [default: myproject] >
Enter the create mode (exec or lib) [default: exec] >
Enter your username [default: username] >
Enter the package name [default: hello] >
Enter your license [default: Apache-2.0] >
Created my-project

2. moon.mod.json增加license和repository字段。

license表示这个mooncakes.io所使用的licencse,必须符合spdx标准。

3. 正式移除moon check --daemon

4. moon publish新增上传大小限制,上传大小必须小于16Mib

其他更新

1. windows平台的安装路径从~/.moon改为~/.moon/bin,与其他平台保持一致。

2. 修复关于newtype goto definition 和 rename 的 bug

weekly 2024-01-08

· 阅读需 2 分钟

MoonBit更新

1. 正式移除 interface 关键字

正式移除了 interface 关键字,使用 trait 代替。

trait

2. 引入let mut id = expr

根据社区的反馈,引入let mut id = expr的语法替代 var id = expr,下周将移除 var id = expr的支持。

mut

3. 给 Array 类型增加了 Default 的实现

例如:

fn init {
debug(Array::default()) // []
}

4. 给 List 类型增加了 DefaultEq、和Debug 的实现

例如:

fn init {
let l1: List[_] = Cons(1, Cons(2, Cons(3, List::default())))
let l2: List[_] = Cons(1, Cons(2, Cons(4, Nil)))
debug(l1) // Cons(1, Cons(2, Cons(3, Nil)))
debug(l1 == l2) // false
debug(l2 == l2) // true
}

5. 修复对pub函数体的类型检查

形如这样的例子:

priv type T
pub fn f() {
let t: T = ... // public definition cannot depend on private type
...
}

之前会在T报错,但现在不会了。

插件更新

1. 新增MoonBit AI

目前已新增MoonBit AI,地址是https://ai.moonbitlang.com,欢迎大家试用。

2. 提高LSP稳定性

修复一些会导致LSP崩溃的bug,提升LSP的稳定性。

构建系统更新

1. 修复 moon test 会测试 .mooncakes 文件夹下的包的问题

2. 废弃 moon check --daemon

3. 改进 moon.pkg.json 格式或内容错误时的错误提示

如下图所示: build-system

weekly 2024-01-02

· 阅读需 8 分钟

MoonBit更新

1. 上线了MoonBit包管理平台 mooncakes.io

详细信息见:https://mp.weixin.qq.com/s/dBA4dA2fKL4FHc6KOcisBg

2. ⽀持了 recursive newtype

可以在MoonBit中实现类型安全的y combinator:

type R[X] (R[X]) -> X

fn y[X, Y](f : ((X) -> Y) -> (X) -> Y) -> (X) -> Y {
fn ff (x: R[(X) -> Y]) -> (X) -> Y {
fn(a) { f(x.0(x))(a) }
}
ff(R::R(fn(x) { fn (a) { f(x.0(x))(a) } }))
}

fn factx(f: ((Int) -> Int)) -> (Int) -> Int {
fn(n: Int) -> Int {
if n <= 1 { 1 } else { n * f(n-1)}
}
}

fn init {
let fact = y(factx)
let n = fact(10)
println(n)
}

3. 新增内置函数sqrt

用于计算二次方根

fn init {
// sqrt 的类型是 Double -> Double
println(sqrt(4.0)) // 2.0
}

4. 新增运算符 ===

用于判断两个值是否引用相等:

fn init {
let x = [1, 3]
let y = [1, 3]
let z = x
if x === y {
println("x === y")
} else if x === z {
println("x === z")
}
// Output: x === z
}

5. method/trait系统的更新:

在过去的几周里,我们对 MoonBit 的方法/接口系统进行了许多设计上的调整,让它的行为更加合理、健壮。下面是现在的方法系统的行为:

  • 方法是和类型关联的函数。可以通过下面的语法定义一个方法
fn T::method(...) -> ... { ... }

// 例如
type MyInt Int
fn MyInt::default() -> MyInt { MyInt(0) }

enum MyList[X] {
Nil
Cons(X, MyList[X])
}

fn MyList::map2[X, Y, R](
f: (X, Y) -> R,
xs: MyList[X],
ys: MyList[Y]
) -> MyList[R] {
...
}

作为一种便捷的语法糖,当函数的第一个参数名为 self 时,Moonbit 会自动将它定义成 self的类型上的方法:

fn add(self: MyInt, other: MyInt) -> MyInt { ... }
// 等价于
fn MyInt::add(x: MyInt, y: MyInt) -> MyInt { ... }
  • 方法都是普通函数。所以在没有歧义时,可以直接当成普通函数调用:
enum MyList[X] { ... }
fn MyList::length[X](xs: MyList[X]) -> Int {
...
}

fn init {
let xs: MyList[_] = ...
debug(length(xs))
}

如果有歧义无法直接调用,也可以用 T::method(...) 的形式显式调用:

struct Type1 { ... } derive(Debug)
fn Type1::default() -> Type1 { ... }

struct Type2 { ... } derive(Debug)
fn Type2::default() -> Type2 { ... }

fn init {
// debug(default()): 有歧义!
debug(Type1::default()) // ok
debug(Type2::default()) // ok
}
  • 当方法的第一个参数就是它所属的类型时,可以使用 x.method(...) 语法来快捷地调用。而且这种调用方式在跨包时不需要写出包名。MoonBit 会自动根据 x 的类型找到正确的方法:
// 名为 @list 的包
pub enum List[X] { ... }
pub fn map[X](self: List[X], f: (X) -> Y) -> List[Y] {
...
}

// 在另一个包中使用 @list
fn init {
let xs: @list.List[_] = ...
// 下面三种写法是等价的
xs.map(...)
@list.map(xs, ...) // 无歧义时可以如此调用
@list.List::map(xs, ...)
}
  • **只有类型所在的包可以给类型定义方法。**这保证了第三方包无法意外或恶意修改现有类型的行为和 trait 系统的一致性。

MoonBit 的 trait 系统的行为变化如下:

  • trait 中的方法声明,任何时候都不再需要 Self:: 前缀。方法的第一个参数是否是 Self 对行为不再有影响

  • 类型可以通过它现有的方法自动地实现一个 trait,不需要手动写出。但如果一个类型没有实现一个 trait,或者原本的实现不能满足需求,需要拓展它的功能,可以用如下的语法定义特殊的拓展方法,用于给一个类型显式地实现某个 trait

// 给 [T] 实现 trait [Eq] 中的 [op_equal] 方法
fn Eq::op_equal(x: T, other: T) -> { ... }

这些拓展方法只能用于实现指定的 trait。例如,上面的拓展方法 Eq::op_equal 只能被用于实现 Eq,不能被用 T::op_equal 或是 t.op_equal(...) 的形式直接调用。在寻找 trait 的实现时,拓展方法的优先级比普通方法高。

  • 只有类型所在的包或 trait 所在的包可以定义拓展方法。因此,某个类型为某个 trait 提供的实现在任何地方都是唯一且确定的。这保证了第三方包不会意外地更改一段程序的行为。

和之前相比,方法/trait 系统最大的不兼容改动是,现在不能给内建和第三方类型直接定义方法了。但通过心得拓展方法的机制,依然可以为内建/第三方类型实现新的 trait 来拓展功能。

构建系统更新

1. moon.pkg.jsonimport 字段增加了数组的表示

数组中要么是一个字符串,要么是一个 object { "path": ..., "alias": ...},比如:

{
"is_main": true,
"import": [
{ "path": "moonbitlang/import004/lib", "alias": "lib" },
"moonbitlang/import004/lib2", // 使用默认的alias: "lib2"
{ "path": "moonbitlang/import004/lib3", "alias": "lib3" },
{ "path": "moonbitlang/import004/lib4", "alias": "" } // 使用默认的alias: "lib4"
]
}

2. moon new现在支持通过交互式方式来创建项目。

  • 创建一个可执行项目
$ moon new
Enter the path to create the project (. for current directory) > myproject
Enter the create mode (exec or lib) > exec
Enter your username > moonbitlang
Enter the package name > hello

上面的命令等价于

 moon new --path myproject --user moonbitlang --name hello

这将会在文件夹 ./myproject 中创建一个名为 moonbitlang/hello 的项目,其目录结构为

.
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── main
│ ├── main.mbt
│ └── moon.pkg.json
└── moon.mod.json
  • 创建一个包
$ moon new
Enter the path to create the project (. for current directory) > mylib
Enter the create mode (exec or lib) > lib
Enter your username > moonbitlang
Enter the package name > hello

上面的命令等价于

 moon new --path mylib --lib --user moonbitlang --name hello

这将会在文件夹 ./mylib 中创建一个名为 moonbitlang/hello 的包,其目录结构为

.
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── moon.mod.json
├── moon.pkg.json
└── top.mbt

weekly 2023-12-25

· 阅读需 3 分钟

MoonBit更新

01. 添加内置类型 Result

enum Result[T, E] {
Ok(T)
Err(E)
}

02. 添加问号操作符

新增了问号操作符,用于简化错误处理:

fn may_fail() -> Option[Int] { ... }

fn compose_may_fail() -> Option[String] {
let x = may_fail()?
let y = may_fail()?.lsr(3)
Some ((x + y).to_string())
}

问号操作符的语义是:如果 t?t 的结果是 None,那么 t? 相当于 return None(直接跳出当前函数)。如果 t?t 的结果是 Some(x),那么 t? 相当于 x。除了 Option 类型,问号操作符也能作用在 Result 类型上:

fn may_error() -> Result[Int, String] { ... }

fn compose_may_error() -> Result[Int, String] {
let x = may_error()?
let y = may_error()?
if y == 0 {
return Err("divide by zero")
}
Ok (x / y)
}

03. 将关键字 interface 修改为 trait

根据社区的反馈,将关键字 interface 修改为 trait,暂时保留对关键字interface的兼容:

04. 修改死代码消除的逻辑

对于顶层 let 在没有被使用的情况下,一律视作可以被删除,无论其是否有副作用。比如,

let a = 1       // will be removed
let b: T = f(a) // will be removed
fn init {
.. // a and b are not used
}

如果函数 f 会触发副作用,需要把 f(a) 这个调用放到 fn init { .. } 中以避免其被删除

05. 修复代码格式化工具不能正确处理中文注释的问题

06.修复不能正确处理中文全局标识符的问题

IDE更新

01. 添加是否自动启动 moon check 的选项

vscode用户可以在settings.json中使用moonbit.autoMoonCheck.enable控制是否自动启动moon check

或者你也可以在设置中搜索MoonBit:

02. 修复 derive(Show) 误报错误的问题

修复前

修复后

构建系统更新

01. 添加 moon doc 命令

添加 moon doc 命令,用于生成和预览文档。moon doc --serve会根据代码中的markdown注释生成文档,并在本地启动网页服务器。访问输出的链接即可查看效果。