MoonBit语法基础概述

改变世界的编程语言 MoonBit

大家好,我是农村程序员,独立开发者,前端之虎陈随易,我的个人网站是 https://chensuiyi.me
这是我的 《改变世界的编程语言MoonBit》 系列文章,将自己学习和理解 MoonBit 的过程分享给大家,希望能带来参考和帮助。
全部文章可以前往 MoonBit 开发网 https://moonbit.edgeone.apphttps://moonbit.pages.dev 查看,我坚信,MoonBit 将会改变世界。

往期文章

前面几篇文章,我们系统地学习了 MoonBit 的背景知识、项目结构、配置系统以及如何在前端开发中使用。今天,我们终于要进入 MoonBit 语言本身的语法学习了!

本文将带你全面了解 MoonBit 的基础语法,包括程序结构、变量绑定、数据类型、函数定义、控制流等核心概念。让我们一起开始这段精彩的语法之旅吧!

程序的基本组成

一个 MoonBit 程序由以下顶层定义组成:

  • 类型定义:定义结构体、枚举等数据类型
  • 函数定义:定义可复用的代码块
  • 常量定义和变量绑定:定义不变的常量和可变的变量
  • init 函数:初始化函数
  • main 函数:程序主入口
  • test:内联测试

类比理解

如果把 MoonBit 程序比作一本书,那么:

  • 类型定义就像是书中的角色设定
  • 函数定义就像是各个章节
  • main 函数就是故事的开头
  • test 块就是每章后面的习题

表达式和语句

MoonBit 区分语句表达式,这是理解 MoonBit 的关键。

表达式:会产生一个值,包括:

  • 值字面量(布尔值、数字、字符、字符串、数组、元组、结构体)
  • 算术、逻辑或比较操作
  • 访问数组元素(如 a[0])、结构体字段(如 r.x)、元组组件(如 t.0
  • 变量和枚举构造器
  • 匿名函数定义
  • matchifloop 表达式等

语句:执行某些操作但不产生值,包括:

  • 命名本地函数定义
  • 本地变量绑定
  • 赋值
  • 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_varcalculate_sum
常量、类型 大写字母开头,建议 PascalCase 或 SCREAMING_SNAKE_CASE MyTypeMAX_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!")
}

布尔值

布尔类型有两个值:truefalse

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

支持 breakcontinue

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:合并两个迭代器

优势filtermap 等方法是惰性的,不会为中间值分配内存。

总结

本文介绍了 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 程序了!

相关推荐
济南壹软网络科技有限公司1 小时前
沉浸式体验革命:壹软科技2025新版盲盒源码前端3D渲染与个性化运营技术解析
前端·科技·uni-app·开源·php·盲盒源码
v***8571 小时前
解决Spring Boot中Druid连接池“discard long time none received connection“警告
spring boot·后端·oracle
v***43171 小时前
springboot3整合knife4j详细版,包会!(不带swagger2玩)
android·前端·后端
程序员西西1 小时前
Spring Cloud实战总结:90%开发者踩过的坑,这篇一次性帮你填平!
java·后端
t***82111 小时前
【Spring】Cookie和Session是什么
java·后端·spring
y***13641 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
用户345848285051 小时前
dict.fromkeys()和OrderedDict具体是如何实现去重的?
后端
e***98571 小时前
Springboot的jak安装与配置教程
java·spring boot·后端
干饭比赛第一名获得者1 小时前
🚀 终极指南:Mac M4 编译 Rust 至 Linux (AMD64)
后端·rust