Ten Features of the MoonBit Language

Original Title: Ten Features of the MoonBit Language

OSCHINA

↑ Click the blue text to follow us

Original Title: Ten Features of the MoonBit Language

Original link: https://medium.com/@hivemind_tech/moonbit-language-in-10-features-4dc41a3a1d6cAuthor: Ignacio | Engineer at Hivemind, a German tech company

Ten Features of the MoonBit Language

As a Scala developer, I have recently noticed that the market for Scala is gradually shrinking, prompting me to explore other programming languages with similar features, such as support for functional programming, higher-order types, higher-order functions, generics, operator overloading, and domain modeling.

Recently, I heard about the MoonBit language on X (formerly Twitter) and learned more through my search. MoonBit is an AI-native general-purpose programming language developed under the leadership of Zhang Hongbo.

Zhang Hongbo has extensive experience in programming language development, having been a core contributor to OCaml, the creator of ReScript, and involved in the development of Flow at Meta (formerly Facebook).

MoonBit is developed by the Digital Economy Institute of the Guangdong-Hong Kong-Macao Greater Bay Area (IDEA), which is dedicated to cutting-edge research and industrial applications in the fields of artificial intelligence and digital economy.

On its official website, I found that MoonBit has the following fundamental features:

  • Combines the advantages of Rust and Scala
  • Does not use the “borrowing” concept from Rust
  • Employs a garbage collection mechanism
  • Exceptional performance
  • Can be compiled to WebAssembly

To experience whether programming in MoonBit feels similar to writing high-quality Scala code, I decided to write some code in MoonBit. I chose a well-known topic for domain modeling: the chessboard. I wanted to define the pieces, the board, and the initial state of the game (without involving the movement of pieces and game logic).

The example code repository can be found at the following link: https://github.com/ignacio-hivemind/MoonBit-chess-example

Next, let’s explore these features of MoonBit one by one.

Ten Features

1. Enumeration Types

First, I created some definitions for the pieces on the chessboard. In chess, pieces can be black or white, and the types include Pawn, Rook, Knight, Bishop, Queen, and 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. pub enum Color { White Black } /// This is documentation for the Piece enum. /// It represents the different pieces in a chess game. pub enum Piece { Pawn Rook Knight Bishop Queen King }

As shown above, using three slashes (///) before a definition adds documentation comments for methods, data types, or functions. Using two slashes (//) indicates a single-line comment. The pub keyword indicates that these definitions are public to other files or modules. The enumeration type (enum) defines a new type whose values can only be the options specified within the braces. For example, the value of Color can only be White or Black, and the value of Piece can only be one of Pawn, Rook, Knight, Bishop, Queen, or King.

2. Automatic Derivation of Built-in Traits

In the previous enum definitions, we can add derive(Show, Eq) to automatically implement the Show and Eq traits for these enums. This means we can directly compare and print instances of Color or Piece.

pub enum Color { .. } derive(Show, Eq) pub enum Piece { .. } derive(Show, Eq)

For example, we can write a function to compare pieces:

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()) } }

In this example, we can directly use the == operator to compare instances of Piece because Piece implements the Eq trait. At the same time, we can use the to_string() method to print instances of Piece because it implements the Show trait.

3. Type Aliases

When defining the chessboard, we can use type aliases to improve code readability and maintainability. For example, defining BoardPlace as Option[(Piece, Color)] indicates that each position on the board is either empty or contains a piece of a specific 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)]

With this definition, we can use BoardPlace anywhere in the code instead of the corresponding Option type, and vice versa. This is just a simplified expression of the type definition on the right. Additionally, it is worth noting that the Option data type is built into the standard library of the MoonBit language, similar to Rust and Scala. MoonBit also has a built-in Result data type, which is similar to the Either type in Scala but focuses more on error handling.

4. Pattern Matching

Pattern matching is a common concept for developers familiar with Haskell, Scala, or Rust. In MoonBit, a function using pattern matching can be defined as follows:

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() } }

Here, pieceToString is a mapping (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") ])

The input of the above function is of type BoardPlace, and the output is a string representing the piece at that position on the board. Additionally, you can use the special wildcard * to match all other cases not matched by the previous patterns.

It is important to note that in MoonBit, both the match and if keywords are expressions, not statements. Therefore, they return a value.

Similar to Scala, in a code block enclosed by curly braces {}, the value of the last expression is the return value of that block. This also applies to functions, for example:

pub fn abs(a: Int) -> Int { let absolute: Int = if a >= 0 { a } else { -a } return absolute }

When the return keyword is omitted, the same effect can be achieved:

pub fn abs(a: Int) -> Int { let absolute: Int = if a >= 0 { a } else { -a } absolute }

However, in certain scenarios, using an explicit return statement is still very useful, especially when you want to return early, skipping the remaining logic of the function for specific cases:

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. Struct Types

Struct types allow the construction of new data types by combining multiple fields of different types. This mechanism is similar to classes in other programming languages, especially when methods and information hiding (encapsulation) are added to structs.

For example, we can define a row on the chessboard (Row) as follows:

/// 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)

Next, we define the grid structure of the entire board (Board) and the current state of the board (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 }

The above definitions clearly express the structure of the board elements and the state of the board.

When we want to add methods under the namespace of the Row struct, there are two ways:

Method 1: This method defines a row of the board without any pieces. Note the Row:: prefix, which clearly indicates that this is a method defined for the Row type.

pub fn Row::empty_row() -> Row { { cols: Array::make(8, None) } }

Method 2: If the method needs to access the data of the struct itself (self), the definition is as follows:

// fn <name>(self: <type>, <parameters>) -> <return type=""> { <body> } // And then you can call: <object>.<name>(<parameters>) pub fn get_turn(self: BoardState) -> Turn { self.turn }</parameters></name></object></body></return></parameters></type></name>

For example, when board_state is an instance of BoardState, we can get the current turn information in the chess game by calling board_state.get_turn().

6. Operator Overloading

By overloading the “[]” operator, you can allow indexing operations on elements in a row of the chessboard, as shown in the following code snippet. You only need to overload the **op_get()** method for your type (in this case, the Row type):

// 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:

pub fn op_set(self: Row, index: Int, value: BoardPlace) -> Unit { self.cols[index] = value; }

For example, now you can do this:

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. New Type Definitions

MoonBit allows you to define a new type based on an existing type. For example, to define the Turn data type, we can do it like this:

/// This is a new type that represents a turn in a chess game. pub type Turn Color

Now, Turn is a new type, similar to opaque types in Scala. To create an instance of the Turn type, you need to wrap the value in the type name:

pub fn BoardState::initialSetup!() -> BoardState { { board: Board::initialize!(), turn: Turn(Color::White) } }

This approach ensures that the values of color (Color) and turn (Turn) are not confused at compile time.

8. Traits

Below is the syntax for defining new traits in MoonBit. Since it is “open”, it can be extended:

/// 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 }

We defined two traits, each with different (abstract) methods: draw() and paint(). This is similar to interfaces in Java or traits in Scala.

The two traits can be combined or inherited using the ” + ” operator:

// This is how you extend and combine traits in MoonBit language. pub trait DrawableAndPaintable : Drawable + Paintable {}

Methods in traits are implemented as follows:

/// Implement Drawable for BoardPlace trait pub impl Drawable for BoardPlace with draw(self: BoardPlace) -> String { ... }

As you can see, I implemented the draw() method on the BoardPlace type (to satisfy the Drawable interface). If we also implement the paint() method for the BoardPlace type, then this data type will also satisfy Paintable and DrawableAndPaintable.

Next, we can also implement the draw() method for the Row type:

/// Implement Drawable for Row impl Drawable for Row with draw(self: Row) -> String { ... }

9. Built-in Testing

By defining a helper function, we can generate a new row of chessboard data from a string:

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 }

This is how to define a for loop in MoonBit, iterating from 0 to 7 (inclusive). I sequentially insert each piece from the input string into the cols array. The assert_eq! statement checks whether the rowStr parameter has a length of 8 to ensure that a row can be constructed correctly. The last line returns a new Row object.

Next, we can define tests anywhere in the code using the test keyword:

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))) }

This approach is very concise, allowing us to embed test blocks directly in the code to verify that certain properties hold true, which is especially helpful during continuous development of new features or refactoring code.

10. Support for Functional Programming

Let’s revisit the new_row_from_string() function defined in the previous section. We originally used a **for** loop to push pieces into the row array one by one, but we can actually use the array’s map function to generate these elements:

pub fn Row::new_row_from_string!(rowStr: String) -> Row { assert_eq!(rowStr.length(), 8) { cols: rowStr.to_array().map(new_place_from_char) }

Now, it’s done in one line!

The logic of this function is: convert the string to a character array, then pass each character to the new_place_from_char() function to generate the cols array. The final expression constructs and returns an instance of the struct containing cols.

Additionally, as an extra feature, MoonBit supports generic data types, which you can use to define collections or parameterized types:

fn count[A](list : @immut/list.T[A]) -> UInt { match list { Nil => 0 Cons(*, rest) => count(rest) + 1 } }

More details about generics and functional programming will be introduced in subsequent articles!

Advantages

1. Garbage Collection

MoonBit is a highly expressive language that shares many similarities with Rust but does not adopt Rust’s memory management concepts of “borrowing” and “ownership”. While these mechanisms provide memory safety, I find them too low-level and not user-friendly. MoonBit uses a garbage collection mechanism to reclaim memory space, making the language more developer-friendly and providing a simpler and more natural coding experience.

2. Toolchain

In this example, I wrote about 400 lines of code, but the tools used to run and test the program (such as the moon command-line tool) and the VS Code plugin felt quite stable and practical, supporting the development of large applications well. The only downside is that the debugger sometimes shows the internal representation of local variables instead of their actual values, which is not very intuitive.

3. Performance

Although I only programmed in MoonBit for a few hours, its compilation and execution speed are very fast! The compiler is primarily optimized for WASM (WebAssembly) but also supports compilation to JavaScript and code for other platforms.

You can check some performance benchmarks on the official MoonBit website (https://www.MoonBitlang.com/):

Ten Features of the MoonBit Language

(The original text includes links and charts; it is recommended to visit the official website for the latest data)

Surprisingly, in some benchmark tests, MoonBit even outperformed Rust and Go. MoonBit can generate compact binary files, which significantly improves loading speed and runtime performance in web environments, making deployment easier, faster, and more cost-effective.

4. Comparison with Scala

The MoonBit language also absorbs many concepts from Scala, such as “the last expression in a code block returns the value”.

MoonBit has a smaller language scale and does not include all the features of Scala. However, considering Scala’s steep learning curve and difficulty in mastering, this may actually be a good thing—because it means that it is easier for development teams to get up to speed quickly. While you won’t have all the functional programming (FP) features, you can still write very good functional code, such as the following code snippet (excerpted from the MoonBit official website):

fn main { resources .iter() .map*option(fn { (name, Text(str)) | (name, CSV(content=str)) => Some((name, str)) (*, Executable) => None }) .map(fn { (name, content) => { let name = name.pad*start(10, ' ') let content = 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 }

You can use lambda expressions, traits, structs (instead of classes), and higher-order functions. Additionally, like in Rust and Scala, MoonBit also has built-in Option and Result data types. Scala is stronger in expressiveness and flexibility but also more complex.

Scala can call all Java libraries—these libraries have been developed over many years, are numerous, and very stable; in contrast, the number of libraries currently available for MoonBit is limited, and their maturity is relatively low (on the official website, there are about 250 libraries available).

The Moon CLI also serves as a package manager, for example: moon add peter-jerry-ye/async. This command tells the project to add a dependency named peter-jerry-ye/async.

5. Community

The community size of MoonBit is not as large as that of Rust or Scala, which means that it is currently more difficult to find resources online, and AI programming assistants (such as LLM and Copilot) do not yet provide sufficient support for MoonBit.

Initially, I thought this language was still very immature, but when I checked its available libraries on https://mooncakes.io/, I found that MoonBit actually covers many foundational libraries, such as HTTP, asynchronous programming, and machine learning tools (like torch).

Additionally, MoonBit has a built-in Json data type, which is very useful for developers who need to handle HTTP JSON services:

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 }

Final Summary

As of March 2025, MoonBit has surpassed the testing phase. Its compiler (including the WebAssembly backend) was open-sourced in December 2024, marking an important step towards a stable version. The MoonBit team is steadily advancing towards the release of version 1.0, which is expected to include support for asynchronous programming and embedded programming capabilities.

With its modern language features, high performance, and small size of generated binaries, MoonBit is very lightweight and cost-effective for deployment in the cloud.

Although MoonBit’s expressiveness is not as rich and concise as Scala’s, it currently competes strongly with Rust in many aspects. This gives MoonBit significant potential for success in certain commercial fields.

MoonBit (https://www.moonbitlang.cn/) is the first industrial-grade programming language and its supporting toolchain in China, developed by the AI-native programming language and developer platform created by the Digital Economy Research Institute of the Guangdong-Hong Kong-Macao Greater Bay Area (referred to as the “IDEA Research Institute”). By innovating frameworks, it has formed a latecomer advantage in the programming language field, successfully leading traditional languages in compilation speed, runtime speed, and binary size.

Recommended Reading:

Open-source programming language MoonBit 2024 Annual Technical Review

AI-native development platform MoonBit (Moon Rabbit) open-sources core compiler

Latest MoonBit WASM output size is much smaller than Rust

Domestic programming language benchmark—MoonBit (Moon Rabbit) releases beta preview version: introducing modern generics system and accurate error handling

Chinese developer team creates AI-native programming language—MoonBit (Moon Rabbit) officially open-sources core library

Ten Features of the MoonBit Language

Leave a Comment