改变世界的编程语言 MoonBit
大家好,我是农村程序员,独立开发者,前端之虎陈随易,我的个人网站是
https://chensuiyi.me
这是我的《改变世界的编程语言MoonBit》系列文章,将自己学习和理解 MoonBit 的过程分享给大家,希望能带来参考和帮助。
全部文章可以前往 MoonBit 开发网https://moonbit.edgeone.app或https://moonbit.pages.dev查看,我坚信,MoonBit 将会改变世界。
往期文章
- 改变世界的编程语言 MoonBit:背景知识速览
- 改变世界的编程语言 MoonBit:项目文件详解
- 改变世界的编程语言 MoonBit:配置系统介绍(上)
- 改变世界的编程语言 MoonBit:配置系统介绍(下)
- 改变世界的编程语言 MoonBit:如何用于前端开发
前面几篇文章,我们系统地学习了 MoonBit 的背景知识、项目结构、配置系统以及如何在前端开发中使用。今天,我们终于要进入 MoonBit 语言本身的语法学习了!
本文将带你全面了解 MoonBit 的基础语法,包括程序结构、变量绑定、数据类型、函数定义、控制流等核心概念。让我们一起开始这段精彩的语法之旅吧!
程序的基本组成
一个 MoonBit 程序由以下顶层定义组成:
- 类型定义:定义结构体、枚举等数据类型
- 函数定义:定义可复用的代码块
- 常量定义和变量绑定:定义不变的常量和可变的变量
init函数:初始化函数main函数:程序主入口test块:内联测试
类比理解:
如果把 MoonBit 程序比作一本书,那么:
- 类型定义就像是书中的角色设定
- 函数定义就像是各个章节
main函数就是故事的开头test块就是每章后面的习题
表达式和语句
MoonBit 区分语句 和表达式,这是理解 MoonBit 的关键。
表达式:会产生一个值,包括:
- 值字面量(布尔值、数字、字符、字符串、数组、元组、结构体)
- 算术、逻辑或比较操作
- 访问数组元素(如
a[0])、结构体字段(如r.x)、元组组件(如t.0) - 变量和枚举构造器
- 匿名函数定义
match、if、loop表达式等
语句:执行某些操作但不产生值,包括:
- 命名本地函数定义
- 本地变量绑定
- 赋值
return语句- 任何返回类型为
Unit的表达式
重要规则:在函数体中,只有最后一个子句应该是一个表达式,它作为返回值。
moonbit
fn foo() -> Int {
let x = 1 // 语句:变量绑定
x + 1 // 表达式:作为返回值
}
通俗理解:
语句就像做事情,比如"把苹果放桌上";表达式就像回答问题,比如"桌上有几个苹果?答:3 个"。
变量绑定
MoonBit 支持三种变量绑定方式:
不可变变量(let)
使用 let 声明的变量不能被重新赋值:
moonbit
fn main {
let x = 10
// x = 20 // ❌ 错误!不可变变量不能重新赋值
println(x)
}
可变变量(let mut)
使用 let mut 声明的变量可以被重新赋值:
moonbit
fn main {
let mut i = 10
i = 20 // ✅ 正确!可变变量可以重新赋值
println(i)
}
常量(const)
使用 const 声明的常量只能在顶层定义,不能在函数内部定义:
moonbit
const ZERO = 0 // ✅ 顶层常量
fn main {
// const LOCAL = 1 // ❌ 错误!常量不能在函数内定义
println(ZERO)
}
注意事项:
- 顶层变量绑定需要显式类型注释(除非使用字符串、字节或数字等字面量定义)
- 顶层变量不能是可变的(如需可变,使用
Ref代替)
命名约定
MoonBit 有一套清晰的命名规范:
| 类型 | 规则 | 示例 |
|---|---|---|
| 变量、函数 | 小写字母开头,建议 snake_case | my_var、calculate_sum |
| 常量、类型 | 大写字母开头,建议 PascalCase 或 SCREAMING_SNAKE_CASE | MyType、MAX_SIZE |
关键字(不能使用)
rust
as, else, extern, fn, fnalias, if, let, const, match, using,
mut, type, typealias, struct, enum, trait, traitalias, derive,
while, break, continue, import, return, throw, raise, try, catch,
pub, priv, readonly, true, false, _, test, loop, for, in, impl,
with, guard, async, is, suberror, and, letrec, enumview, noraise, defer
保留关键字(会引发警告)
rust
module, move, ref, static, super, unsafe, use, where, await,
dyn, abstract, do, final, macro, override, typeof, virtual, yield...
通俗理解:
命名规范就像给孩子取名字,有些名字(关键字)是被语言"占用"的,你不能用;有些名字虽然目前可以用,但可能将来会被占用(保留关键字)。
程序入口
init 函数
init 函数是特殊的初始化函数:
- 没有参数列表也没有返回类型
- 同一个包中可以有多个
init函数 - 不能被显式调用或被其他函数引用
- 在初始化包时会隐式自动调用
moonbit
fn init {
let x = 1
println(x)
}
main 函数
main 函数是程序的主入口,在初始化阶段之后执行:
moonbit
fn main {
let x = 2
println(x)
}
运行以上两段代码,输出:
1
2
注意 :只有标记为 is-main: true 的包才能定义 main 函数。
test 块
test 块用于定义内联测试:
moonbit
test "test_name" {
assert_eq(1 + 1, 2)
assert_eq(2 + 2, 4)
inspect([1, 2, 3], content="[1, 2, 3]")
}
通俗理解:
init就像开餐厅前的准备工作(备菜、摆桌子)main就像正式开门营业test就像品控检查,确保菜品质量
内置数据类型
MoonBit 提供了丰富的内置数据类型:
Unit 类型
Unit 表示没有有意义的值,类似于 C/C++ 的 void,但它是一个真正的类型:
moonbit
fn print_hello() -> Unit {
println("Hello, world!")
}
布尔值
布尔类型有两个值:true 和 false:
moonbit
let a = true
let b = false
let c = a && b // 逻辑与
let d = a || b // 逻辑或
let e = not(a) // 逻辑非
数字类型
MoonBit 支持多种数字类型:
| 类型 | 说明 | 示例 |
|---|---|---|
Int16 |
16 位有符号整数 | (42 : Int16) |
Int |
32 位有符号整数 | 42 |
Int64 |
64 位有符号整数 | 1000L |
UInt16 |
16 位无符号整数 | (14 : UInt16) |
UInt |
32 位无符号整数 | 14U |
UInt64 |
64 位无符号整数 | 14UL |
Double |
64 位浮点数 | 3.14 |
Float |
32 位浮点数 | (3.14 : Float) |
BigInt |
大整数 | 10000000000000000000000N |
数字字面量支持多种进制:
moonbit
let decimal = 1_000_000 // 十进制,支持下划线分隔
let binary = 0b110010 // 二进制
let octal = 0o1234 // 八进制
let hex = 0xA_B_C // 十六进制
类型自动重载:
moonbit
let int : Int = 42
let double : Double = 42 // 自动转换为 42.0
let bigint : BigInt = 42 // 自动转换为大整数
字符串
字符串包含一系列 UTF-16 码点:
moonbit
let a = "兔rabbit"
println(a[0].to_char()) // Some('兔')
println(a[1].to_char()) // Some('r')
多行字符串:
使用 #| 前缀保留原始字符串,使用 $| 前缀支持转义和插值:
moonbit
let lang = "MoonBit"
let b =
#| Hello
#| ---
$| \{lang}\n
#| ---
字符串插值:
moonbit
let x = 42
println("The answer is \{x}") // The answer is 42
转义序列:
| 转义序列 | 含义 |
|---|---|
\n |
换行 |
\r |
回车 |
\t |
制表符 |
\b |
退格 |
\\ |
反斜杠 |
\u5154 |
Unicode 转义(4 位) |
\u{1F600} |
Unicode 转义(可变长) |
字符
Char 表示一个 Unicode 码点:
moonbit
let a : Char = 'A'
let b = '兔'
let zero = '\u0030' // '0'
let emoji = '\u{1F600}' // 😀
当期望类型是 Int 时,字符字面量可以重载为 Int 类型:
moonbit
let s : String = "hello"
let diff = s[0] - 'a' // 字符 'a' 被重载为 Int
字节
字节字面量是一个 ASCII 字符或转义序列:
moonbit
let b1 : Byte = b'a'
let b2 = b'\xff'
// 字节序列
let bs : Bytes = b"abcd"
元组
元组是使用圆括号构造的有限值集合:
moonbit
fn pack(a : Bool, b : Int, c : String) -> (Bool, Int, String) {
(a, b, c)
}
fn main {
let quad = pack(false, 100, "text")
let (bool_val, int_val, str) = quad // 解构
println("\{bool_val} \{int_val} \{str}")
// 也可以通过索引访问
let x = quad.0 // false
let y = quad.1 // 100
}
数组
数组是使用方括号构造的有限值序列:
moonbit
let numbers = [1, 2, 3, 4]
fn main {
let a = numbers[2] // 访问第3个元素
numbers[3] = 5 // 修改第4个元素
println(a + numbers[3]) // 8
}
两种数组类型:
Array[T]:可增长的数组FixedArray[T]:固定大小的数组
ArrayView(数组视图):
moonbit
let xs = [0, 1, 2, 3, 4, 5]
let s1 = xs[2:] // [2, 3, 4, 5]
let s2 = xs[:4] // [0, 1, 2, 3]
let s3 = xs[2:5] // [2, 3, 4]
Map(映射)
moonbit
let map : Map[String, Int] = { "x": 1, "y": 2, "z": 3 }
Option 和 Result
用于表示可能的错误或失败:
moonbit
let a : Int? = None // 没有值
let b : Option[Int] = Some(42) // 有值
let c : Result[Int, String] = Ok(42) // 成功
let d : Result[Int, String] = Err("error") // 失败
Ref(可变引用)
Ref[T] 是包含类型 T 的值的可变引用:
moonbit
let a : Ref[Int] = { val: 100 }
fn main {
a.val = 200
println(a.val) // 200
}
函数定义
顶层函数
使用 fn 关键字定义顶层函数,需要显式类型注释:
moonbit
fn add3(x : Int, y : Int, z : Int) -> Int {
x + y + z
}
局部函数
局部函数可以是命名的或匿名的,类型可以自动推断:
moonbit
fn main {
// 命名局部函数
fn inc(x) {
x + 1
}
// 匿名函数
let double = fn(x) { x * 2 }
// 箭头函数(更简洁)
[1, 2, 3].each(x => println(x * x))
println(inc(5)) // 6
println(double(5)) // 10
}
函数应用
moonbit
add3(1, 2, 7) // 10
// 任何求值为函数值的表达式都是可应用的
let w = (if true { fn(x) { x + 1 } } else { fn(x) { x + 2 } })(3)
// w = 4
部分应用
使用 _ 操作符进行部分应用:
moonbit
fn add(x : Int, y : Int) -> Int {
x + y
}
fn main {
let add10 = add(10, _) // 创建一个新函数
println(add10(5)) // 15
println(add10(10)) // 20
}
带标签的参数
moonbit
fn labelled(arg1~ : Int, arg2~ : Int) -> Int {
arg1 + arg2
}
fn main {
let arg1 = 1
// 可以使用 label=value 或 label~ 简写
println(labelled(arg2=2, arg1~)) // 3
}
可选参数
moonbit
fn optional(opt? : Int = 42) -> Int {
opt
}
fn main {
println(optional()) // 42(使用默认值)
println(optional(opt=0)) // 0
}
没有默认值的可选参数
没有默认值的可选参数类型为 T?,默认值为 None。调用时 MoonBit 会自动用 Some 包装:
moonbit
fn new_image(width? : Int, height? : Int) -> Unit {
if width is Some(w) {
println("width: \{w}")
} else {
println("no width")
}
}
fn main {
new_image(width=1920, height=1080)
}
转发可选参数时,使用 label?=value 语法,label? 是 label?=label 的简写:
moonbit
fn fixed_width_image(height? : Int) -> Unit {
new_image(width=1920, height?) // 转发 height 参数
}
控制结构
条件表达式(if)
moonbit
let initial = if size < 1 { 1 } else { size }
注意:条件表达式在 MoonBit 中始终返回一个值,结果和 else 子句的返回值必须是相同的类型。
匹配表达式(match)
moonbit
fn decide_sport(weather : String) -> String {
match weather {
"sunny" => "tennis"
"rainy" => "swimming"
_ => "unknown" // 默认情况
}
}
卫语句(guard)
用于检查指定的不变量:
moonbit
fn guarded_get(array : Array[Int], index : Int) -> Int? {
guard index >= 0 && index < array.length() else { None }
Some(array[index])
}
结合 is 表达式使用:
moonbit
fn getProcessedText(resources : Map[String, Resource], path : String) -> String {
guard resources.get(path) is Some(resource) else {
fail("\{path} not found")
}
guard resource is PlainText(text) else {
fail("\{path} is not plain text")
}
process(text)
}
While 循环
moonbit
fn main {
let mut i = 5
while i > 0 {
println(i)
i = i - 1
}
}
// 输出: 5 4 3 2 1
支持 break 和 continue:
moonbit
fn main {
let mut i = 5
while i > 0 {
i = i - 1
if i == 4 { continue } // 跳过本次
if i == 1 { break } // 退出循环
println(i)
}
}
// 输出: 3 2
支持 else 子句:
moonbit
fn main {
let mut i = 2
while i > 0 {
println(i)
i = i - 1
} else {
println("循环结束,i = \{i}")
}
}
// 输出: 2 1 循环结束,i = 0
For 循环
C 风格的 for 循环:
moonbit
fn main {
for i = 0; i < 5; i = i + 1 {
println(i)
}
}
// 输出: 0 1 2 3 4
多变量绑定:
moonbit
for i = 0, j = 0; i + j < 100; i = i + 1, j = j + 1 {
println(i)
}
for..in 循环
遍历数据结构的便捷语法:
moonbit
for x in [1, 2, 3] {
println(x)
}
遍历整数范围:
moonbit
for j in 0..<10 { // 0 到 9
println(j)
}
for l in 0..=10 { // 0 到 10(包含)
println(l)
}
遍历 Map:
moonbit
for k, v in { "x": 1, "y": 2, "z": 3 } {
println("\{k}: \{v}")
}
遍历数组(带索引):
moonbit
for index, elem in [4, 5, 6] {
println("第 \{index + 1} 个元素是 \{elem}")
}
函数式循环(loop)
moonbit
fn sum(xs : @list.List[Int]) -> Int {
loop (xs, 0) {
(Empty, acc) => break acc
(More(x, tail=rest), acc) => continue (rest, x + acc)
}
}
defer 表达式
用于可靠的资源释放:
moonbit
fn main {
defer println("释放资源")
println("使用资源")
}
// 输出:使用资源
// 释放资源
连续的 defer 以倒序执行:
moonbit
fn main {
defer println("第一处 defer")
defer println("第二处 defer")
println("做些事情")
}
// 输出:做些事情
// 第二处 defer
// 第一处 defer
自定义数据类型
结构体(struct)
moonbit
struct User {
id : Int
name : String
mut email : String // 可变字段
}
fn main {
let u = User::{ id: 0, name: "John Doe", email: "john@doe.com" }
u.email = "john@doe.name" // 修改可变字段
println(u.name)
}
简写构造:
moonbit
let name = "john"
let email = "john@doe.com"
let u = { id: 0, name, email } // 字段名与变量名相同时可简写
结构体更新语法:
moonbit
let user = { id: 0, name: "John", email: "john@doe.com" }
let updated = { ..user, email: "new@email.com" } // 只更新 email
枚举(enum)
moonbit
enum Relation {
Smaller
Greater
Equal
}
fn compare_int(x : Int, y : Int) -> Relation {
if x < y { Smaller }
else if x > y { Greater }
else { Equal }
}
带数据的枚举:
moonbit
enum Lst {
Nil
Cons(Int, Lst) // 携带额外数据
}
fn is_singleton(l : Lst) -> Bool {
match l {
Cons(_, Nil) => true
_ => false
}
}
带标签参数的构造器:
moonbit
enum Point {
Point2D(x~ : Int, y~ : Int)
}
fn main {
let p = Point2D(x=10, y=20)
match p {
Point2D(x~, y~) => println("x=\{x}, y=\{y}")
}
}
元组结构体
moonbit
struct UserId(Int)
struct UserInfo(UserId, String)
fn main {
let id = UserId(1)
let info = UserInfo(id, "John")
// 通过索引访问
println(id.0) // 1
println(info.1) // John
}
类型别名
moonbit
pub type MyIndex = Int
pub type MyMap = Map[Int, String]
模式匹配
简单模式
moonbit
const ONE = 1
fn match_int(x : Int) -> Unit {
match x {
0 => println("zero")
ONE => println("one")
value => println(value) // 绑定到变量
}
}
使用 _ 作为通配符,.. 忽略剩余字段:
moonbit
struct Point3D { x : Int, y : Int, z : Int }
fn match_point(p : Point3D) -> Unit {
match p {
{ x: 0, .. } => println("在 yz 平面上")
_ => println("不在 yz 平面上")
}
}
数组模式
moonbit
fn match_array(arr : Array[Int]) -> Unit {
match arr {
[] => println("空数组")
[a, b, c] => println("三元素: \{a}, \{b}, \{c}")
[a, .. rest, b] => println("首: \{a}, 尾: \{b}")
}
}
范围模式
moonbit
fn classify_char(c : Char) -> String {
match c {
'a'..='z' => "lowercase"
'A'..='Z' => "uppercase"
'0'..='9' => "digit"
_ => "other"
}
}
Map 模式
moonbit
fn match_map(map : Map[String, Int]) -> Unit {
match map {
{ "b": _, .. } => println("包含 key 'b'")
{ "a": x, .. } => println("a 的值是 \{x}")
_ => println("其他情况")
}
}
守卫条件
moonbit
fn guard_example(x : Int?) -> Int {
match x {
Some(a) if a >= 0 => a
Some(b) => -b
None => -1
}
}
泛型
泛型在顶层函数和数据类型定义中受支持:
moonbit
enum List[T] {
Nil
Cons(T, List[T])
}
fn[S, T] List::map(self : List[S], f : (S) -> T) -> List[T] {
match self {
Nil => Nil
Cons(x, xs) => Cons(f(x), xs.map(f))
}
}
fn[S, T] List::reduce(self : List[S], op : (T, S) -> T, init : T) -> T {
match self {
Nil => init
Cons(x, xs) => xs.reduce(op, op(init, x))
}
}
特殊语法
管道操作符(|>)
moonbit
5 |> ignore // 等价于 ignore(5)
[] |> Array::push(5) // 等价于 Array::push([], 5)
1 |> add(5) |> ignore // 链式调用
级联运算符(..)
用于连续对同一值执行多个操作:
moonbit
let result = StringBuilder::new()
..write_char('a')
..write_char('b')
..write_string("cdef")
.to_string()
is 表达式
测试值是否符合特定模式:
moonbit
fn is_none[T](x : T?) -> Bool {
x is None
}
fn start_with_lower(s : String) -> Bool {
s is ['a'..='z', ..]
}
// 在 if 中使用
fn process(x : Array[Int?]) -> Unit {
if x is [v, ..] && v is Some(i) && i is (0..=10) {
println("匹配成功:\{i}")
}
}
展开运算符(..)
在构造数组时展开元素序列:
moonbit
let a1 = [1, 2, 3]
let a2 = [4, 5, 6]
let combined : Array[Int] = [..a1, ..a2, 7, 8]
// [1, 2, 3, 4, 5, 6, 7, 8]
TODO 语法(...)
用于标记尚未实现的代码:
moonbit
fn todo_func() -> Int {
... // 占位符,表示待实现
}
迭代器
MoonBit 内置了 Iter[T] 类型,是一种内部迭代器:
moonbit
fn filter_even(l : Array[Int]) -> Array[Int] {
l.iter()
.filter(x => (x & 1) == 0)
.collect()
}
fn fact(n : Int) -> Int {
(1).until(n).fold(Int::mul, init=1)
}
常用方法:
each:遍历每个元素fold:折叠操作collect:收集到数组filter:过滤元素map:转换元素concat:合并两个迭代器
优势 :filter、map 等方法是惰性的,不会为中间值分配内存。
总结
本文介绍了 MoonBit 语言的基础语法,包括:
| 概念 | 要点 |
|---|---|
| 程序结构 | 由类型定义、函数定义、变量绑定、init/main/test 组成 |
| 变量绑定 | let(不可变)、let mut(可变)、const(常量) |
| 数据类型 | Unit、布尔、数字、字符串、数组、元组、Map、Option、Result 等 |
| 函数 | 顶层函数、局部函数、匿名函数、标签参数、可选参数 |
| 控制流 | if、match、guard、while、for、for..in、loop、defer |
| 自定义类型 | struct、enum、元组结构体、类型别名 |
| 模式匹配 | 简单模式、数组模式、范围模式、Map 模式、守卫条件 |
| 泛型 | 类型参数化的函数和数据类型 |
| 特殊语法 | 管道 ` |
MoonBit 融合了函数式和命令式编程的优点,语法简洁优雅,类型系统强大。掌握这些基础语法后,你就可以开始编写真正的 MoonBit 程序了!