原標題:MoonBit 語言的十大特性(MoonBit Language in 10 Features)
OSCHINA
原標題:MoonBit 語言的十大特性(MoonBit Language in 10 Features)
原文鏈接:
https://medium.com/@hivemind_tech/moonbit-language-in-10-features-4dc41a3a1d6c
作者:Ignacio丨德國科技公司 Hivemind 工程師

作為一名Scala開發(fā)者,我最近注意到Scala的市場在逐漸萎縮,這促使我探索其他具有類似特性的編程語言,例如支持函數(shù)式編程、高階類型、高階函數(shù)、泛型、運算符重載和領(lǐng)域建模等。
最近,我在 X(前稱Twitter)上聽說了MoonBit語言,并通過搜索了解了更多信息。MoonBit是一種AI原生的通用編程語言,由張宏波領(lǐng)導開發(fā)。
張宏波在編程語言開發(fā)方面有著豐富的經(jīng)驗,曾是 OCaml 的核心貢獻者,ReScript的創(chuàng)建者,并在 Meta (前稱 FaceBook)公司參與了 Flow 的開發(fā)。
MoonBit 由粵港澳大灣區(qū)數(shù)字經(jīng)濟學院(IDEA)開發(fā),該機構(gòu)致力于人工智能和數(shù)字經(jīng)濟領(lǐng)域的前沿研究和產(chǎn)業(yè)應(yīng)用。
在其官方網(wǎng)站上,我發(fā)現(xiàn)MoonBit具有以下基本特性:
?融合了Rust和Scala的優(yōu)點
?不使用Rust中的“借用”概念
?采用垃圾回收機制
?性能卓越
?可編譯為WebAssembly
為了體驗MoonBit的編程感受是否類似于編寫高質(zhì)量的Scala代碼,我決定用MoonBit編寫一些代碼。我選擇了一個眾所周知的主題進行領(lǐng)域建模:國際象棋棋盤。我希望定義棋子、棋盤以及游戲的初始狀態(tài)(暫不涉及棋子的移動和游戲邏輯)。
示例代碼庫可在以下鏈接找到:
https://github.com/ignacio-hivemind/MoonBit-chess-example
接下來,讓我們逐一探討MoonBit的這些特性。
十大特性1、枚舉類型
首先,我為棋盤上的棋子創(chuàng)建了一些定義。在國際象棋中,棋子可以是黑色或白色,種類包括兵(Pawn)、車(Rook)、馬(Knight)、象(Bishop)、后(Queen)和王(King)。
// This application is about a chess board,
// and how to represent it in the MoonBit language.
//
// Board is a double dimension array of BoardPlace
// cols: 0 1 2 3 4 5 6 7
// row 0: R N B Q K B N R
// row 1: P P P P P P P P
// row 2: . . . . . . . .
// row 3: . . . . . . . .
// row 4: . . . . . . . .
// row 5: . . . . . . . .
// row 6: p p p p p p p p
// row 7: r n b q k b n r
//
// The upper case letters represent the white pieces,
// whereas the lower case letters represent the black pieces.
// The pieces are: Pawn (P or p), Rook (R or r), Knight (N or n),
// Bishop (B or b), Queen (Q or q), King (K or k).
// The dots represent empty places.
/// This is documentation for the Color enum data type.
/// This is the color of the pieces in a chess game.
pubenumColor {
White
Black
}
/// This is documentation for the Piece enum.
/// It represents the different pieces in a chess game.
pubenumPiece {
Pawn
Rook
Knight
Bishop
Queen
King
}
如上所示,在定義前使用三個斜杠(///)可為方法、數(shù)據(jù)類型或函數(shù)添加文檔注釋。使用兩個斜杠(//)則表示單行注釋。pub關(guān)鍵字表示這些定義對其他文件或模塊是公開的。枚舉類型(enum)定義了一種新的類型,其值只能是大括號內(nèi)指定的選項。例如,Color的值只能是White或Black,Piece的值只能是Pawn、Rook、Knight、Bishop、Queen或King之一。
2、內(nèi)置Trait的自動派生
在之前的枚舉定義中,我們可以添加derive(Show, Eq),自動為這些枚舉實現(xiàn)Show和Eq特性。這意味著我們可以直接比較和打印Color或Piece的實例。
pub enum Color {
..
} derive(Show, Eq)
pub enum Piece {
..
} derive(Show, Eq)
例如,我們可以編寫一個函數(shù)來比較棋子:
pub enum Color {
..
} derive(Show, Eq)
pub enum Piece {
..
} derive(Show, Eq)
pub fn compare_pieces(piece: Piece) -> Unit {
if piece == Pawn {
println("The piece is a pawn")
} else {
println("The piece is a " + piece.to_string())
}
}
在這個示例中,我們可以直接使用==運算符比較Piece的實例,因為Piece實現(xiàn)了Eq特性。同時,我們可以使用to_string()方法打印Piece的實例,因為它實現(xiàn)了Show特性。
3、類型別名
在定義棋盤時,我們可以使用類型別名來提高代碼的可讀性和可維護性。例如,定義BoardPlace為Option[(Piece, Color)],表示棋盤上的每個位置要么為空,要么包含一個特定顏色的棋子。
/// This is the representation of a place on a chess board.
/// It can be empty (None) or contain a piece with a color: Some((piece, color)).
pub typealias BoardPlace = Option[(Piece, Color)]
通過這種定義方式,在代碼中任何位置,我們都可以用BoardPlace代替對應(yīng)的Option類型,反之亦然。這只是右側(cè)類型定義的簡化表達方式。另外,值得注意的是,Option數(shù)據(jù)類型內(nèi)置于MoonBit語言的標準庫中,與Rust和Scala類似。MoonBit還內(nèi)置了Result數(shù)據(jù)類型,它與Scala中的Either類型類似,但更專注于錯誤處理。
4、模式匹配
模式匹配(Pattern Matching) 對熟悉Haskell、Scala或Rust的開發(fā)者而言,“模式匹配”是一個常見概念。在MoonBit中,可以通過如下方式定義一個使用模式匹配的函數(shù):
fn draw(self: BoardPlace) -> String {
match self {
None => "." // empty place
Some((piece, Color::White)) => pieceToString.get*or_default(piece, ".")
Some((piece, Color::Black)) => pieceToString.get_or_default(piece, ".").to_lower()
}
}
這里,pieceToString是一個映射(map):
let pieceToString: Map[Piece, String] = Map::of([
(Piece::Pawn, "P"),
(Piece::Rook, "R"),
(Piece::Knight, "N"),
(Piece::Bishop, "B"),
(Piece::Queen, "Q"),
(Piece::King, "K")
])
上述函數(shù)的輸入是BoardPlace類型,輸出則是表示棋盤上該位置棋子的字符串。此外,你還可以使用特殊的通配符 *,來匹配所有未被前面的模式匹配到的其他情況。
需要注意的是,在MoonBit中,match和if關(guān)鍵字都是表達式(expressions),而非語句(statements)。因此,它們會返回一個值。
與Scala類似,在一個由花括號{}圍成的代碼塊中,最后一個表達式的值即為該代碼塊的返回值。這一點在函數(shù)中同樣適用,例如:
pub fn abs(a: Int) -> Int {
let absolute: Int = if a >= 0 { a } else { -a }
return absolute
}
當省略掉return關(guān)鍵字時,也能達到完全相同的效果:
pub fn abs(a: Int) -> Int {
let absolute: Int = if a >= 0 { a } else { -a }
absolute
}
然而,在某些場景中,使用顯式的return語句仍然是非常有用的,特別是當你希望提前返回(early return),跳過函數(shù)剩余邏輯處理特定情況時:
pub fn early_return(a: String) -> Bool {
if a == "." {
return false
}
// go on with the function logic:
// at this point you know that a is NOT “.”
// ...
}
5、結(jié)構(gòu)體類型結(jié)構(gòu)體(struct)類型允許通過組合多個不同類型的字段來構(gòu)造出新的數(shù)據(jù)類型。這種機制類似于其他編程語言中的類(class),特別是在結(jié)構(gòu)體中加入方法定義以及信息隱藏(封裝)時,更是如此。
例如,我們可以這樣定義棋盤上的一行(Row):
/// This is a struct that represents a row in the board
pub struct Row {
// Array type definition:
priv cols: Array[BoardPlace] // information hiding: private fields
} derive(Show, Eq)
再定義整個棋盤(Board)的網(wǎng)格結(jié)構(gòu)以及棋盤當前的狀態(tài)(BoardState):
/// This is a struct that represents the board grid
pub struct Board {
priv grid: Array[Row]
}
/// This is a struct that represents the board state
pub struct BoardState {
priv board: Board
priv turn: Turn
}
以上定義清晰地表達了棋盤元素及棋盤狀態(tài)的結(jié)構(gòu)。
當我們想在Row這個結(jié)構(gòu)體的命名空間(namespace)下添加方法時,有兩種方式:
方法一: 此方法定義了一個沒有任何棋子的棋盤行。注意Row::這個前綴,它明確表明這是針對類型Row定義的方法。
pub fn Row::empty_row() -> Row {
{ cols: Array::make(8, None) }
}
方式二: 如果方法需要訪問結(jié)構(gòu)體自身(self)的數(shù)據(jù),定義方式則如下:
// fn (self: , ) -> { }
// And then you can call: . ( ) pub fn get_turn( self: BoardState) -> Turn { self.turn } 例如,當board_state是BoardState類型的實例時,我們就可以通過 board_state.get_turn() 來獲取當前國際象棋游戲中的回合(Turn)信息。 6、運算符重載 可以通過重載“[]”運算符,以允許對棋盤行中的元素進行索引操作,如下面的代碼片段所示。你只需為你的類型(在本例中為Row類型)重載**op_get()**方法即可:
// This special method name "op_get" is used to overload the [] operator.
pub fn op_get(self:Row, index: Int) -> BoardPlace {
self.cols[index]
}To allow for indexed assignment operations, you can override the op_set() method: 為了允許索引賦值操作,你還可以重載 op_set() 方法:
pub fn op_set(self: Row, index: Int, value: BoardPlace) -> Unit {
self.cols[index] = value;
} 例如,現(xiàn)在你可以這樣做:
pub fn check_first_element(row: Row) -> Unit {
let element: BoardPlace = row[0] // Access the row with an index using “[]” operator
if element is Some((Piece::Pawn, Color::White)) {
println("First element is a white pawn")
}
...
} 7、新類型定義 MoonBit允許你基于已有的類型定義一個新類型。例如,要定義Turn數(shù)據(jù)類型,我們可以這樣做:
/// This is a new type that represents a turn in a chess game.
pub type Turn Color 現(xiàn)在,Turn就是一個新類型,類似于Scala語言中的opaque類型。要創(chuàng)建一個Turn類型的實例,你需要將值包裝在類型名中:
pub fn BoardState::initialSetup!() -> BoardState {
{ board: Board::initialize!(), turn: Turn(Color::White) }
} 這種方式確保了顏色(Color)和回合(Turn)的值在編譯時不會被混淆。 8、特性 下面是MoonBit中定義新特性的語法。由于它是“open”的,因此可以被擴展:
/// This trait defines a draw method that returns a string
/// representation of the object.
/// It is used to draw the different objects in the chess game to a String.
/// (although it could be in another format or different resource, like a file or
/// screen).
pub(open) trait Drawable {
draw(Self) -> String
}
pub(open) trait Paintable {
paint(Self) -> Unit
} 我們定義了兩個特性,每個特性中都有不同的(抽象)方法:draw() 和 paint()。這類似于 Java 中的接口或 Scala 中的 trait。 兩個特性可以通過“+”運算符進行組合或繼承:
// This is how you extend and combine traits in MoonBit language.
pub trait DrawableAndPaintable : Drawable + Paintable {} 特性中的方法通過以下方式進行實現(xiàn):
/// Implement Drawable for BoardPlace trait
pub impl Drawable for BoardPlace with draw(self: BoardPlace) -> String {
...
} 如你所見,我在 BoardPlace 類型上實現(xiàn)了 draw() 方法(以滿足 Drawable 接口的要求)。如果我們同樣為 BoardPlace 類型實現(xiàn)** paint() ** 方法,那么該數(shù)據(jù)類型也將滿足** Paintable ** 和 DrawableAndPaintable 。 接下來,我們還可以為 Row 類型實現(xiàn) draw() 方法:
/// Implement Drawable for Row
impl Drawable for Row with draw(self: Row) -> String {
...
} 9、內(nèi)置測試 通過定義一個輔助函數(shù),我們可以根據(jù)字符串生成一行新的棋盤數(shù)據(jù):
pub fn Row::new_row_from_string!(rowStr: String) -> Row {
assert_eq!(rowStr.length(), 8)
let cols = []
// for loops in MoonBit
for i in 0..=7 {
cols.push(new_place_from_char(rowStr[i]))
}
{ cols: cols }
} 這是在 MoonBit 中定義 for 循環(huán)的方式,用于從 0 到 7(包含7)進行迭代。我將輸入字符串中的每個棋子依次插入到 cols 數(shù)組中。assert_eq! 語句用于檢查 rowStr 參數(shù)的長度是否為 8,以確??梢哉_構(gòu)造出一行。最后一行返回一個新的 Row 對象。 接下來,我們可以在代碼的任何位置使用 test 關(guān)鍵字定義測試:
test "create a white row from string" {
let my_row: Row = Row::new_row_from_string!("RNBQKBNR")
assert*eq!(my_row[0], Some((Piece::Rook, Color::White)))
assert_eq!(my_row[1], Some((Piece::Knight, Color::White)))
assert_eq!(my_row[2], Some((Piece::Bishop, Color::White)))
assert_eq!(my_row[3], Some((Piece::Queen, Color::White)))
assert_eq!(my_row[4], Some((Piece::King, Color::White)))
assert_eq!(my_row[5], Some((Piece::Bishop, Color::White)))
assert_eq!(my_row[6], Some((Piece::Knight, Color::White)))
assert_eq!(my_row[7], Some((Piece::Rook, Color::White)))
} 這種方式非常簡潔,我們無需依賴其他測試框架,就可以直接在代碼中嵌入測試塊,用來驗證某些性質(zhì)是否一直成立,特別是在持續(xù)開發(fā)新功能或重構(gòu)代碼時非常有幫助。 10、函數(shù)式編程支持 讓我們回顧上一節(jié)中定義的 new_row_from_string() 函數(shù)。我們原本使用** for** 循環(huán)逐個將棋子壓入行數(shù)組中,但其實可以使用數(shù)組的 map 函數(shù)來生成這些元素:
pub fn Row::new_row_from_string!(rowStr: String) -> Row {
assert_eq!(rowStr.length(), 8)
{ cols: rowStr.to_array().map(new_place_from_char) }
} 現(xiàn)在,它變成了一行搞定! 這個函數(shù)的邏輯是:將字符串轉(zhuǎn)換為字符數(shù)組,然后逐個字符傳入 new_place_from_char() 函數(shù),用以生成 cols 數(shù)組。最后的表達式構(gòu)造并返回一個包含 cols 的結(jié)構(gòu)體實例。 另外,作為一個額外的特性,MoonBit 支持泛型數(shù)據(jù)類型,你可以用它來定義集合或參數(shù)化類型:
fn count[A](list : @immut/list.T[A]) -> UInt {
match list {
Nil => 0
Cons(*, rest) => count(rest) + 1
}
} 更多關(guān)于泛型和函數(shù)式編程的細節(jié)將在后續(xù)文章中介紹! 優(yōu)勢 1、垃圾回收 MoonBit 是一種表達能力非常強的語言,在許多方面與 Rust 相似,但不采用 Rust 中“借用”和“所有權(quán)”的內(nèi)存管理概念。雖然這些機制能帶來內(nèi)存安全,但在我看來它們太底層、使用起來也不夠友好。而 MoonBit 使用的是垃圾回收機制來回收內(nèi)存空間,這使得語言對開發(fā)者更友好,編碼體驗也更加簡潔自然。 2、工具鏈 本次示例我只寫了大約 400 行代碼,但用來運行和測試程序的工具(如 moon 命令行工具)以及 VS Code 插件,給我的感覺是相當穩(wěn)定、實用,能夠很好地支持大型應(yīng)用的開發(fā)。唯一的不足是調(diào)試器有時會顯示局部變量的內(nèi)部表示形式,而不是它們實際的值,這不太直觀。 3、性能表現(xiàn) 雖然我只用 MoonBit 編程了幾個小時,但它的編譯和運行速度都非???!編譯器主要面向 WASM(WebAssembly)優(yōu)化,但也支持編譯為 JavaScript 和其他平臺的代碼。 你可以在 MoonBit 官方網(wǎng)站(https://www.MoonBitlang.com/)查看一些性能基準測試: (此處原文附有鏈接和圖表,建議前往官網(wǎng)獲取最新數(shù)據(jù)) 令人驚訝的是,在一些基準測試中,MoonBit 的表現(xiàn)甚至超過了 Rust 和 Go。MoonBit 能夠生成體積緊湊的二進制文件,這在 Web 環(huán)境中能顯著提升加載速度和運行性能,使部署變得更容易、更快速、更具成本效益。 4、與 Scala 的對比 MoonBit 語言同樣吸收了許多來自 Scala 的概念,比如“代碼塊返回最后一個表達式的值”。 MoonBit 的語言規(guī)模更小,也并未包含 Scala 中的所有特性。但考慮到 Scala 的學習曲線陡峭、精通難度較高,這反而可能是件好事——因為這意味著更容易讓開發(fā)團隊快速上手。雖然你不會擁有所有的函數(shù)式編程(FP)特性,但依然可以編寫出非常不錯的函數(shù)式代碼,例如以下代碼片段(摘自 MoonBit 官網(wǎng)):
fn main {
resources
.iter()
.map*option(fn {
(name, Text(str)) | (name, CSV(content=str)) => Some((name, str))
(*, Executable) => None
})
.map(fn {
(name, content) => {
letname = name.pad*start(10, ' ')
letcontent = content
.pad_end(10, ' ')
.replace_all(old="\n", new=" ")
.substring(start=0, end=10)
"\{name}: \{content} ..."
}
})
.intersperse("\n")
.fold(init="Summary:\n", String::op_add)
|> println
} 你可以使用 Lambda 表達式、特性(traits)、結(jié)構(gòu)體(structs,代替類)以及高階函數(shù)。此外,就像在 Rust 和 Scala 中一樣,MoonBit 也內(nèi)建了 Option 和 Result 數(shù)據(jù)類型。Scala 在表達能力和靈活性方面更強,但也更復雜。 Scala 還能調(diào)用所有 Java 的庫——這些庫經(jīng)過多年發(fā)展,數(shù)量龐大且非常穩(wěn)定;相比之下,MoonBit 當前可用的庫數(shù)量不多,成熟度也相對較低(在官方網(wǎng)站上,大約有 250 個左右的庫可供使用)。 Moon CLI 也作為包管理器使用,例如:moon add peter-jerry-ye/async。這條命令告訴項目添加一個名為 peter-jerry-ye/async 的依賴項。 5、社區(qū) MoonBit 的社區(qū)規(guī)模尚不如 Rust 或 Scala,那意味著目前在網(wǎng)上找資料會比較困難,AI 編程助手(如 LLM 和 Copilot)對 MoonBit 的支持也還不夠完善。 起初,我認為這個語言還非常不成熟,但當我在 https://mooncakes.io/ 上查看其可用庫時,發(fā)現(xiàn)其實 MoonBit 已經(jīng)涵蓋了許多基礎(chǔ)領(lǐng)域的庫,例如 HTTP、異步編程、機器學習工具(如 torch)等。 此外,MoonBit 還內(nèi)置了 Json 數(shù)據(jù)類型,這對于開發(fā)需要處理 HTTP JSON 服務(wù)的程序員來說非常實用:
fn main {
let json_example : Json = {
"array": ["a", "b"],
"age": 22,
"name": "John",
"boolean": True
}
let greeting = match json_example {
{ "age": Number(age), "name": String(name) } => "Hello \{name}. You are \{age}"
* => "not match"
}
greeting |> println
} 最后總結(jié) 截至2025年3月,MoonBit 已經(jīng)超越測試階段。其編譯器(包括 WebAssembly 后端)已于2024年12月開源,這標志著向穩(wěn)定版本邁出了重要一步。MoonBit 團隊正在穩(wěn)步推進 1.0 正式版的發(fā)布,預計將包括對異步支持和嵌入式編程能力的集成。 憑借其現(xiàn)代化的語言特性、高性能以及生成的二進制文件體積小,MoonBit 在部署到云端時非常輕便且成本低。 盡管 MoonBit 的表達能力不如 Scala 豐富、簡潔,因此暫時還不能完全取代 Scala,但它目前在很多方面可以與 Rust 相抗衡。這使得 MoonBit 在某些商業(yè)領(lǐng)域具備強大的成功潛力。 MoonBit(https://www.moonbitlang.cn/)是國內(nèi)首個工業(yè)級編程語言及其配套工具鏈,由粵港澳大灣區(qū)數(shù)字經(jīng)濟研究院(簡稱“IDEA 研究院”)基礎(chǔ)軟件中心打造的 AI 原生的編程語言以及開發(fā)者平臺。通過創(chuàng)新框架在程序語言界形成后發(fā)優(yōu)勢,在編譯速度、運行速度、體積大小上已成功領(lǐng)先傳統(tǒng)語言。 推薦閱讀:
熱門跟貼