MoonBit 0.8.0 Released

We are excited to announce the release of MoonBit 0.8.0.
MoonBit is an AI native programming language. It's reliable,readable and fast.. This release marks an important milestone on MoonBit’s path toward stability and production use.
MoonBit 0.8 is not a simple collection of incremental changes. It represents a clear transition from an experimental language to an engineering-grade language and toolchain. Significant improvements have been made across language semantics, error handling, package management, and developer tooling—making MoonBit better suited for large-scale codebases and agent-centric development workflows.
Why MoonBit 0.8 Matters?
As many developers have observed, Rust provides a solid foundation for AI-assisted development through its strict semantics and strong correctness guarantees. While continuing to pursue similar reliability goals, MoonBit places additional emphasis on much faster compilation speeds—often 10–100× faster than Rust in practical use—as well as a development toolchain deeply integrated with agent-based workflows.
With the release of version 0.8, these design goals are no longer abstract principles. They are now consistently reflected across the language, compiler, runtime, and IDE.
Key Updates
WasmGC/Native/LLVM Backend Backtrace Support
The MoonBit wasmg-gc/native/LLVM backend now supports automatically printing call stacks when a program crashes. Backtraces are mapped directly to the corresponding MoonBit source locations, significantly improving the debugging experience.
RUNTIME ERROR: abort() called
/path/to/moonbitlang/core/array/array.mbt:187 at @moonbitlang/core/array.Array::at[Int]
/path/to/pkg/main/main.mbt:3 by @username/hello/out_of_idx.demo
/path/to/pkg/main/main.mbt:9 by main
AI-Native Specification Support
MoonBit introduces the declare keyword, which can be used to declare types, functions, and other program elements that are intended to be implemented later. If a declare declaration does not have a corresponding implementation, the MoonBit compiler will emit a warning.
The declare keyword provides AI-native specification support. By writing specifications in the form of declare, developers can clearly describe the interfaces and behavior they expect AI systems to implement. AI tools only need to read the declare specifications and the associated test files to begin generating code.
During development, the compiler’s warning messages guide the AI to progressively complete the required implementations. Since missing declare definitions are treated as warnings rather than errors, AI systems can incrementally write and test code as the implementation evolves.
Completed Technical Changelog in MoonBit 0.8
Language Updates
-
The
suberror Err PayloadTypesyntax has been deprecated. Users need to migrate to theenum-like sytax for declaring error type:suberror Err { Err(PayloadType) }This change can be migrated automatically via
moon fmt -
Type inference for builtin error constructors (maily
Failure) is deprecated to avoid scope pollution. When the expected error type is unknown,raise Failure(..)should be migrated toraise Failure::Failure(..), similarly forcatch. -
Values of type
FuncRef[_]can now be called directly from MoonBit code. This feature can be used for dynamic symbol loading or implementing JIT in native backend. -
MoonBit's wasm-gc, native backend and LLVM backend now supports backtrace. When a MoonBit program panic (performing an out-of-bound array indexing operation, failure of
guardstatement withoutelse, ortry!expression receiving an error, or manually callingpanic/abort, etc.), the stack trace of the panic will be printed under debug mode. Here's an example:fn demo(a: Array[Int], b: Array[Int]) -> Unit { let _ = a[1] let _ = b[2] } fn main { let a = [1, 2] let b = [3] demo(a, b) }Take native backend as example, when run with
moon run --target native, the program will output:RUNTIME ERROR: abort() called /path/to/moonbitlang/core/array/array.mbt:187 at @moonbitlang/core/array.Array::at[Int] /path/to/pkg/main/main.mbt:3 by @username/hello/out_of_idx.demo /path/to/pkg/main/main.mbt:9 by mainNote: Native/LLVM backend stacktrace has not been supported on Windows Platform.
-
A new keyword
declareis introduced to replace the previous#declaration_onlyattribute. In addition,declarenow supports declaring trait implementations. For example:declare type T // declare a type to be implemented declare fn T::f(x : T) -> Int // declare a method to be implemented struct S(Int) declare impl Show for S // declare an impl relation -
for .. inloop now supports iterating over a reversed range via reversed range expressionsx>..yandx>=..y:///| test "reversed range, exclusive" { let result = [] for x in 4>..0 { result.push(x) } debug_inspect(result, content="[3, 2, 1, 0]") } ///| test "reversed range, inclusive" { let result = [] for x in 4>=..0 { result.push(x) } debug_inspect(result, content="[4, 3, 2, 1, 0]") }To make the syntax for consistent, the syntax
x..=yfor forward, closed range expression has been migrated tox..<=y, the old syntax is deprecated. This change can be migrated automatically viamoon fmt -
Using
{ ..old_struct, field: .. }to update astructwithprivfields (outside its package) is now forbidden -
lexmatchexpressions in first-match mode now supports pattern guard.lexmatchwith guard currently has some performance penalty, so it is recommended to use it during fast prototying, and evaluate iflexmatchguard should be removed later. The syntax is the same as pattern guard for normalmatch, see https://github.com/moonbitlang/lexmatch_spec for more details:lexmatch input { ("#!" "[^\n]+") if allow_shebang => ... ... } -
structnow supports user defined constructors. The syntax is as follows:struct S { x : Int y : Int // declare a constructor for the `struct` fn new(x~ : Int, y? : Int) -> S } // implement the constructor for `struct` fn S::new(x~ : Int, y? : Int = x) -> S { { x, y } } // using the `struct` constructor test { let s = S(x=1) }
The semantic of struct constructors is:
structconstructors can be declared by adding afn newdeclartion to the end of the body ofstruct. The signature of the constructor has no restriction, except that it must return thestructitself. So features such as optional arguments, raising error can be used freely in thestructconstructor. The parameters offn new(..)inside thestructmust not specify default value for optional arguments, but may omit parameter names- For
structwith type parameters,fn newcan specialize the type parameters or add trait bounds to them. The syntax is the same as a normal toplevel function declaration - If a
structdeclares a constructor viafn new, the constructor must be implemented by adding afn S::newmethod declaration in the same package. The signature ofS::newmust be exactly the same as thefn newdeclaration in thestruct - Using the
structconstructor is exactly the same as using anenumconstructor, except thatstructconstructors cannot be used for pattern matching. For example, when the expected type is known,S(..)can be used directly in place of@pkg.S(..)or@pkg.S::S(..). - The visibility of
structconstructor is the same as the fields of thestruct. So the constructors ofpub structandpub(all) structwill be public, while the constructors ofstructandpriv structwill be private
-
Alias generated by
usingcan now bedeprecatedby adding #deprecated attribute to theusingdeclaration. -
A new trait
Debugis introduced, with auto deriving support.Deriveis a more advanced version of theShowtrait, it can convert MoonBit values to more structural and readable text message:///| struct Data { pos : Array[(Int, Int)] map : Map[String, Int] } derive(Debug) ///| test "pos" { debug_inspect( { pos: [(1, 2), (3, 4), (5, 6)], map: { "key1": 100, "key2": 200, "key3": 300 }, }, content=( #|{ #| pos: [(1, 2), (3, 4), (5, 6)], #| map: { #| "key1": 100, #| "key2": 200, #| "key3": 300, #| }, #|} ), ) }derive(Debug)additionally supports anignoreparameter, which accepts one or more type names. Values with an ignored type will be displayed as...In the derivedDebugimplementation. This is especially useful when working with types from third party packages that have noDebugimplementation:///| struct Data1 { field1 : Data2 field2 : Double field3 : Array[Int] } derive(Debug(ignore=[Data2, Array])) ///| struct Data2 { content : String } ///| test "pos" { debug_inspect( { field1: { content: "data string" }, field2: 10, field3: [1, 2, 3] }, content=( #|{ #| field1: ..., #| field2: 10, #| field3: ..., #|} ), ) }In addition to more readable format with automatic line break, the
moonbitlang/core/debugpackage also provides anassert_eq(a, b)function, which automatically calculate and display the diff of a and b when they are not equal. In the future, we will gradually deprecatederive(Show)and migrate toDebugfor debugging. TheShowtrait will focus on hand-written, domain specific printing logic, such asJson::stringify -
Constructors with arguments can no longer be used as higher order functions directly. An anonymous function wrapper is necessary:
test { let _ : (Int) -> Int? = Some // removed let _ : (Int) -> Int? = x => Some(x) // correct way let _ : Int? = 42 |> Some // pipes are not affected }This behavior has been deprecated for some time. Notice that constructors with arguments can still be used on the right hand side of the pipe operator.
-
Effect inference has been deprecated for local
fn. If afnmay raise error or performasyncoperations in its body, it must be explicitly annotated withraise/async, otherwise the compiler will emit a warning, which will turn into an error in the future. The arrow function syntax(..) => ..is not affected. So, we recommended using arrow functions instead offnfor callback functions in the future. fn can be used when explicit annotation is desirable for documentation or readability purpose. -
The semantic for
x..f()has been adjusted back to its original, simple semantic: x..f() is equivalent to{ x.f(); x }. Previously, the result ofx..f()can be silently ignored. The compiler will now emit a warning for this kind of usage. Users should replace the last..f()replaced with.f(), or explicitly ignore the result. -
for/for .. in/whileloops previously support adding anelseblock to do something when the loop exits normally. To make the syntax more intuitive, theelsekeyword for these loops is replaced withnobreak:fn f() -> Int { for i = 0; i < 10; i = i + 1 { } nobreak { i } }This change can be migrated automatically via
moon fmt. -
A new warning
unnecessary_annotation(disabled by default) is introduced, which identifies unnecessary annotation onstructliteral and constructors (i.e. places where the compiler can automatically deduce the correct type from the context).
Toolchain Updates
-
The moon.pkg DSL is now officially recommended way to write package configurations, and the old moon.pkg.json format is deprecated. You can use moon fmt to automatically migrate to the new moon.pkg format, and new MoonBit projects will now use the moon.pkg format by default. Here's a nexample for some common configuration options:
import { "path/to/pkg1", "path/to/pkg2" @alias, } warnings = "+deprecated-unused_value" -
moon testnow supports using the-jparameter to run tests in parallel -
moon testcan now list all tests to run via the--outlineparameter -
moon test --indexcan now specify a range of test index (inclusive on the left and exclusive on the right). For example,moon test /path/to/test/file.mbt --index 0-2will now run the first two tests in/path/to/test/file.mbt -
The old behavior of
moon install(install all dependencies of current project) has been deprecated, becausemoon checkandmoon buildnow automatically install dependencies. The new behavior ofmoon installis similar to cargo install or go install. It allows users to globally install one or more binaries from mooncakes.io, a git repo, or a local project. The installed package must support native backend and must haveis-mainset to true in its package configuration. For example:moon install username/package (when the project root is a package) moon install username/cmd/main (install a specific package) moon install username/... (install all packages with the specified prefix) moon install ./cmd/main (local path) moon install https://github.com/xxx/yyy.git (git URLs are automatically detected)More usage can be found in
moon install --help -
moon.pkgnow supports aregex_backendoption to specify the backend oflexmatch:options( // The default is "auto", other available options are // "block", "table" and "runtime". // // The "block" backend has the best performance, // but generates larger code // // The "table" backend generates a lookup table at compile time, // and interpret the table dynamically at runtime. // It hits a good balance between performance and code size. // // The "runtime" backend generates code that // use the `regex_engine` package in `moonbitlang/core` // to compile and execute regular expressions dynamically. // It can save a lot of code size when regex is used heavily, // but has the poorest performance. regex_backend: "runtime", ) -
Previously,
moon -C <path>does not change the working directory ofmoon, and interpret all othe path arguments relative to current working directory, which is inconsistent with many other common build systems. Nowmoon -C <path>will change the working directory ofmoon. As a consequence,-Cmust appear before the command line subcommand and all other command line arguments. A new command line option--manifest-pathis added, which receive the path ofmoon.mod.json, and work with that project in current working directory. It can be used to run executables or tests of a MoonBit project in an alternative directory. -
moon runandmoon buildnow use--debugby default -
The front matter syntax for declaring dependencies of
.mbt.mdfiles has been updated. Previously, only module level dependency can be declared, and all packages in those modules will be imported implicitly, which may result in package alias conflict. The the new version,.mbt.mdcan declare package import directly in the front matter header, with support for package alias. The version of the modules to import should be explicitly written in the import path. If a module appears multiple times in the import list, its version need to specified only once. Dependencies inmoonbitlang/coredo not need a version number:--- moonbit: import: - path: moonbitlang/async@0.16.5/aqueue alias: aaqueue backend: native --- -
Templates for
moon neware simplified, and some simple introduction about skills is added. -
A new subcommand
moon fetchcan be used to fetch the source code of a package without adding at as a dependency to current project. The fetched source code is saved in the.reposdirectory under project root or current working directory by default. This is useful for AI agents to learn about available third party packages. -
moon fmtwill now preserve empty line between statements. But multiple consecutive empty lines will be compressed into one empty line:// before formatting fn main { e() // comment f() g() h() }// after formatting fn main { e() // comment f() g() h() } -
moonbitinside.mbt.mdfiles and docstring will now not get type checked.moon fmtwill automatically convertmoonbittomoonbit nocheckautomatically.
Standard and Experimental Library Updates
moonbitlang/asyncChanges:
- Introduce
@process.spawn, which spawns a foreign process inside aTaskGroup, and returns a handle containing the PID of the process. By default, the task group will wait for the process to terminate. If the task group needs to exit early, the foreign process will be terminated automatically - Introduce
@fs.File::{lock, try_lock, unlock}, which provides advisory file lock support (i.e. normal IO operations do not interact with these locks) - Introduce
@fs.tmpdir(prefix~), which creates a temporary directory with the given prefix in the system temporary file store - Introduce
@async.alland@async.any, which are similar toPromise.allandPromise.anyin JavaScript - Add more
examplesto theexamplesdirectory and a brief introduction to these examples
@json.inspecthas been migrated tojson_inspect
IDE Updates
-
Goto definition for alias will now display the definition of the alias itself as well as its original definition:

-
moon idenow supports a new subcommandmoon ide hover, which display the signature and document for a symbol in the source code:$ moonide hover -no-check filter -loc hover.mbt:14 test { let a: Array[Int] = [1] inspect(a.filter((x) => {x > 1})) ^^^^^^ ```moonbit fn[T] Array::filter(self : Array[T], f : (T) -> Bool raise?) -> Array[T] raise? ``` --- Creates a new array containing all elements from the input array that satisfy the given predicate function. Parameters: * `array` : The array to filter. * `predicate` : A function that takes an element and returns a boolean indicating whether the element should be included in the result. Returns a new array containing only the elements for which the predicate function returns `true`. The relative order of the elements is preserved. Example: ```mbt check test { let arr = [1, 2, 3, 4, 5] let evens = arr.filter(x => x % 2 == 0) inspect(evens, content="[2, 4]") } ``` } -
moon ideintroduces a new subcommandmoon ide rename, which generates a patch that renames a symbol. The format of the patch is compatible with theapply_patchtool of OpenAI codex.moon ide renameallows AI agents to perform large scale code refactor robustly and efficiently.$ moon ide rename TaskGroup TG *** Begin Patch *** Update File: /Users/baozhiyuan/Workspace/async/src/async.mbt @@ /// and will result in immediate failure. #deprecated("use `async fn main` or `async test` instead") #cfg(target="native") -pub fn with_event_loop(f : async (TaskGroup[Unit]) -> Unit) -> Unit raise { +pub fn with_event_loop(f : async (TG[Unit]) -> Unit) -> Unit raise { @event_loop.with_event_loop(() => with_task_group(f)) } *** Update File: /Users/baozhiyuan/Workspace/async/src/task_group.mbt @@ /// /// The type parameter `X` in `TaskGroup[X]` is the result type of the group, /// see `with_task_group` for more detail. -struct TaskGroup[X] { +struct TG[X] { children : Set[@coroutine.Coroutine] parent : @coroutine.Coroutine mut waiting : Int @@ pub suberror AlreadyTerminated derive(Show) ///| -fn[X] TaskGroup::spawn_coroutine( +fn[X] TG::spawn_coroutine( - self : TaskGroup[X], + self : TG[X], f : async () -> Unit, ...