Skip to main content

2025-06-03

Β· 4 min read

Language Updates​

1. Omission of ! for Effectful Calls​

Calling effectful functions (those that may throw errors or are asynchronous) no longer requires explicitly appending !. The IDE will automatically underline functions that may throw and render asynchronous functions in italics using semantic highlighting.

2. f?(..) Replaced by try? f(..) Expression​

try? expr is equivalent to try Ok(expr) catch { err => Err(err) }, allowing errors to be converted into the Result type. Compared to the original f?(..), try? is more flexible and can handle multiple function calls with potential errors at once, for example:

try? {
  f(..)
  g(..)
}

When migrating, special attention should be paid to the following case: f?(g!(..)) cannot be simply rewritten as try? f(g(..)), because this would redirect the error from g into the Result. In this case, the result of g should first be extracted with let, and then passed into try?.

3. Add Support for Error Polymorphism in Higher-Order Functions​

fn[X] Array::each(arr: Array[X], f: (X) -> Unit?Error) -> Unit?Error {
  for x in arr {
    f(x)
  }
}

fn main {
  let arr = ["a", "b", "c"]
  arr.each(println) // no error
  println(try? arr.each(fail)) // outputs Err(Failure)
}

The syntax for error polymorphism is (..) -> T?Error. At the call site, ?Error can be replaced with an actual error type, or it can be elided to represent a case where no error may occur.

Specifically, in the example above,

  • If the argument f does not throw, ?Error will be replaced with "no error", and the entire each call will not throw either.
  • If f throws an error, then ?Error will be replaced with the actual error type of f, and the entire each call will throw the same type of error.

4. Deprecation of fn meth(self: T) as Both Method and Function​

Previously, methods defined in the form fn meth(self: T) acted as both methods and regular functions, and could be invoked using either meth(..) or @pkg.meth(..). This behavior treating methods as regular functionsis now deprecated. The compiler will issue a warning when such usage is detected.

As a result of this change, the recommended API design is:

  • Always define methods using the form fn T::meth(..). Avoid using fn meth(self: T) in new code. (This syntax itself may be removed in the future.)

    • Any API that is associated with a specific type should be defined as a method, unless there is a compelling reason not to.

5. The dot syntax now supports anonymous functions using _, written as _.meth(..).​

At the same time, the right-hand side of the pipe operator |> also supports this form of anonymous method calls.

When using this syntax, it's important to ensure that the type of _ can be determined from the context; otherwise, the method cannot be resolved.

Example:

fn main {
  let array_of_array = [ [], [ 1 ], [ 1, 2 ] ]
  let lens = array_of_array.map(_.length())
  lens |> _.each(println)
}

Compared with writing x.meth(..) directly, the advantage of x |> _.meth(..) is that it allows method calls to be embedded into an existing pipeline. For example: x |> regular_func(..) |> _.meth(..).

6. In method declarations using the fn TypeName::meth(..)​

you can use Self to refer to TypeName, which helps shorten the type signature.

type MyLongTypeName Int

fn MyLongTypeName::to_string(x : Self) -> String {
  x._.to_string()
}

If TypeName has parameters, using Self will also require those parameters to be provided.

7. The behavior of implementing trait via method definitions has been officially removed.​

8. The position of type parameters in function declarations has been changed from fn f[..] to fn[..] f, making it consistent with impl.​

This change can be automatically migrated using the formatting tool.

9. The format of methods in .mbti files generated by moon info has changed.​

Previously, all methods were merged into a single large impl block. Now, each method is listed individually in .mbti, consistent with MoonBit's own syntax.

10. The syntax for asynchronous functions has been adjusted back to async(..) -> T, and !Async syntax (used for error handling and async) has been removed due to incompatibility.​

This change can also be automatically migrated using the formatting tool.

11. The literal suffix for Float types now supports .314f.​

Standard Library Updates​

  • The Show::output implementation for Char has changed. Now, all non-printable characters will be escaped, including: Control, Format, Surrogate, Private Use, Unassigned, and Separator (except space).

Toolchain Updates​

  • A single .mbt.md file now supports external dependencies.

Usage example:

---
moonbit:
  deps:
    moonbitlang/x: 0.4.23
    # moonbitlang/x:
    #   path: "/Users/flash/projects/x"  # local deps
  backend:
    js
---

2025-05-19

Β· 2 min read

Language Updates​

The syntax of x..f(..) will be updated. In the case of .. chaining, the last return value will automatically be discarded if it’s not used. For example, the following code:​

impl[X : Show, Y : Show] Show for (X, Y) with output(self, logger) {
  logger
  ..write_string("(")
  ..write_object(self.0)
  ..write_string(", ")
  ..write_object(self.1)
  // Previously, omitting the final `..` could result in a type mismatch,
  // expecting `&Logger` but returning `unit`
  .write_string(")")
}

Can now be simplified as:

impl[X : Show, Y : Show] Show for (X, Y) with output(self, logger) {
  logger
  ..write_string("(")
  ..write_object(self.0)
  ..write_string(", ")
  ..write_object(self.1)
  // You can now write the full chain all the way to the end
  ..write_string(")")

However, if you're directly using the return value of x..f(..), this behavior will be removed. You must now explicitly save the value. For example:

let arr = []..push(1)..push(2)

Must now be rewritten as:

let arr = []
arr..push(1)..push(2)

Structs and enums now support field-level documentation comments, which will be displayed accordingly during doc generation:​

///| Location enum
struct Location {
  /// X coordinate
  x : Int
  /// y coordinate
  y : Int
}

///| Variant enum
enum Variant {
  /// Stirng constructor
  String(String)
  /// Number constructor
  Number(Double)
}

@bytes.View and @string.View will now be compiled as value types in the C and wasm1 backends.​

This means that these two types will no longer involve heap allocation, leading to significantly improved memory layout and performance.

Toolchain Updates​

  • vscode now supports semantic token highlighting, which distinguishes between effectful functions (such as those that throw exceptions) and normal ones, as well as async functions. This gives each usage a different visual style.

  • The build system now supports the virtual package feature. By defining a package as virtual, you can specify a set of interfaces and allow users to choose actual implementations. If no implementation is provided, the system uses a default. This helps achieve clean decoupling and flexible substitution. Note: This feature is still under development. For more details, please refer to: Introducing virtual package in MoonBit

  • Now supports running test and debugging codelen directly on individual .mbt and .mbt.md files.

2025-05-06

Β· 2 min read

Language Updates​

  • [Breaking Change] The way traits are implemented will change. In the future, traits must be explicitly implemented for a type A using impl T for A { ... }. Simply defining methods on A with the same signature as those in trait T will no longer be considered as implementing the trait T for A. This change was previously accompanied by a compiler warning and will soon take effect officially.

  • A new syntactic sugar has been introduced to allow the use of _ as a placeholder for omitted parameters when creating anonymous functions, simplifying their syntax. For example, f(a, _, c, _) is equivalent to fn(b, d) { f(a, b, c, d) }. Supported usage scenarios include:

    • Constructor(args, _), lhs |> Constructor(args, _)
    • function(args, _), lhs |> function(args, _)
    • object.method(args, _) (Note: _.method(args) is not supported yet)
  • fnalias now supports creating aliases for types and trait methods:

    trait Floating {
      sin(Self) -> Self
      cos(Self) -> Self
    }
    
    // You can now use `sin` and `cos` directly without qualifying with `Floating::`
    pub fnalias Floating::(sin, cos)
  • All pragmas have been removed and will be completely replaced by attributes going forward.

  • The #internal attribute has been implemented to provide warnings for external users of a public API:

    /// in moonbitlang/core
    #internal(unsafe, "message")
    pub fn unsafe_get(args){...}
    
    /// in user/module
    fn main {
      unsafe_get(...) // warning!
    }

    Users can disable these warnings by setting the alert option in moon.pkg.json.

  • A new warning has been added for potentially ambiguous use of loop arguments inside a loop block:

    fn main {
      let a = "asdf"
      loop a {
        [k, .. b] => continue a // warning
        [k, .. b] as a => continue a // suggested
        [] => ()
      }
    }
  • Implicit type conversions are now supported from Array to ArrayView, and from Bytes to @bytes.View.

Toolchain Updates​

  • The moon CLI now supports the bench subcommand for running performance benchmarks.

    Use a test block with a b : @bench.T parameter to define a benchmark. You can use b.keep() to prevent the compiler from optimizing away side-effect-free computations:

    fn fib(n : Int) -> Int {
      if n < 2 {
        return n
      }
      return fib(n - 1) + fib(n - 2)
    }
    
    test (b : @bench.T) {
      b.bench(fn() { b.keep(fib(20)) })
    }

    Run benchmarks using:

    $ moon bench
    [..]
    time (mean Β± Οƒ)         range (min … max)
      21.67 Β΅s Β±   0.54 Β΅s    21.28 Β΅s …  23.14 Β΅s  in 10 Γ—   4619 runs

    For more detailed usage instructions, refer to https://docs.moonbitlang.com/en/latest/language/benchmarks.html.

2025-04-21

Β· 4 min read

Language Updates​

  • Async function syntax: The call syntax for async functions has changed to match the error style: f!(...). The old f!!(...) syntax will now trigger a warning.

  • Operator overloading migration to traits: Operator overloading is moving from method-based to trait-based. Going forward, to overload an operator, you must impl the corresponding trait from @moonbitlang/core/builtin. You can find the list of operator-related traits in the language docs and operators.mbt.

    Migration details:

    • Old method-based operator overloading still works, but will raise a compiler warning.

    • Migrate by replacing op_xxx methods with the appropriate impl of the trait.

      • Operator traits enforce stricter type signaturesβ€”for example, the - operator requires the return type to be the same as the input type. If an operator doesn’t match the expected signature, it must be rewritten as a regular method instead of using operator syntax.
    • If a trait defines op_xxx methods, they can be removed and replaced by adding the operator trait to the super trait list. For example:

      // Old version
      trait Number {
        from_int(Int) -> Self
        op_add(Self, Self) -> Self
        op_sub(Self, Self) -> Self
      }
      
      // Migrated version
      trait Number : Add + Sub {
        from_int(Int) -> Self
      }
  • New = _ syntax for traits: You can now mark whether a method in a trait has a default implementation using = _:

    trait Hash {
      hash_combine(Self, Hasher) -> Unit
      hash(Self) -> Int = _ // indicates that `hash` has a default implementation
    }

    a. If a method is marked with = _, a matching default impl Trait with some_method(..) must exist. If a method has a default impl but is not marked with = _, the compiler will warn you.

    b. This is mainly for readability: now you can see from the trait definition which methods have defaults. Why not include the default code directly in the trait? To keep the trait definition short and focused on method signatures.

  • Implicit conversion from String to @string.View is now supported. Also, slicing with [:] now returns a full view again. General slicing like [i:j] is still under design.

    fn f(v : @string.View) -> Unit {
      ignore(v)
    }
    
    fn main {
      f("hello")
      f("world"[:])
    }
  • Pattern matching on @string.View/@bytes.View now supports directly matching against string/byte literals:

    test {
      let s = "String"
      inspect!(s.view(end_offset=3) is "Str", content="true")
      let s : Bytes = "String"
      inspect!(s[:3] is "Str", content="true")
    }
  • [Breaking Change] Updates to the @string core package:

    • Many APIs now use @string.View instead of String for parameters.
    • Some return types also changed from String to View.

    Notable changes:

    Old SignatureNew Signature
    self.replace(old~: String, new~: String) -> Stringself.replace(old~: View, new~: View) -> String
    self.trim(charset: String) -> Stringself.trim(charset: View) -> View
    self.split(substr: String) -> Iter[String]self.split(substr: View) -> Iter[View]
    self.index_of(substr: String, from~: Int) -> Intself.find(substr: View) -> Option[Int]
    self.last_index_of(substr: String, from~: Int) -> Intself.rev_find(substr: View) -> Option[Int]
    self.starts_with(substr: String) -> Boolself.has_prefix(substr: View) -> Bool
    self.ends_with(substr: String) -> Boolself.has_suffix(substr: View) -> Bool
  • Core Json type will become read-only: In the future, enum constructors for Json will no longer be available. Instead, helper functions are provided as replacements:

    test {
      let num = Json::number(3.14)
      let str = Json::string("Hello")
      let obj = Json::object({ "hello": num, "world": str })
    }

Toolchain Updates​

  • IDE drops support for moonbit: true front matter in Markdown:

    • Now, only files with the .mbt.md extension will be treated as MoonBit Markdown files in the IDE.
  • IDE supports setting debug breakpoints in .mbt.md directly:

    • You no longer need to enable Debug: Allow Breakpoint Everywhere in VSCode settings.
  • New scripts field in moon.mod.json for build scripts:

    • Currently supports a postadd script: if a module contains a postadd field, it will be automatically executed after moon add.
      • To skip execution, set the MOON_IGNORE_POSTADD environment variable.

    Example:

    {
      "scripts": {
        "postadd": "python3 build.py"
      }
    }
  • Improvements to .mbt.md Markdown support in the moon CLI:

    • moon check now includes Markdown checking automatically.
    • moon test now includes Markdown tests by default. (The --md flag will be removed in the future.)

2025-04-07

Β· 2 min read

Language Updates​

Wasm Backend: extern type T Now Storable in Arrays and Data Structures​

The wasm backend now supports storing values of extern type T in arrays and other data structures. At FFI boundaries (import/export function signatures), extern type T is still compiled to WebAssembly's externref type.

C FFI Supports borrow​

C FFI now supports the borrow attribute. You can use #borrow(args, ...) above an extern "c" function to customize how MoonBit manages the lifetime of certain arguments, where args is a subset of the C FFI function’s parameter names.

By default, MoonBit assumes C FFI functions are responsible for releasing the passed arguments. This often requires writing helper functions to manually release values.

fn open(path: Bytes, flags: Int, mode: Int) -> Int = "open"
int open_wrapper(moonbit_bytes_t path, int flags, int mode) {
  int rc = open(path, flags, mode);
  moonbit_decref(path);
  return rc;
}

With the borrow attribute, you can instruct MoonBit not to generate reference counting instructions for the specified arguments, allowing direct binding to C library functions without the need for extra helper functions.

#borrow(path)
fn open(path: Bytes, flags: Int, mode: Int) -> Int = "open"

Thanks to #borrow, MoonBit will automatically release path after calling open.

type & trait Support the #deprecated Attribute​

type and trait now support the #deprecated attribute. In the next release, the old pragma-based mechanism will be removed. It's recommended to use the attribute instead:

/// the @alert pragma is deprecated
/// @alert deprecated "message"
fn f() -> Unit { ... }

/// use the #deprecated attribute instead
#deprecated("message")
fn f() -> Unit { ... }

Backend Consistency Checks for Declarations of FFI extern Functions​

Backend consistency checks have been added to declarations of FFI extern functions. For example, the following function will raise an error when building with non-Native backends:

extern "c" fn open(path: Bytes, flags: Int, mode: Int) -> Int = "open"

Toolchain Updates​

  1. Starting this week, toolchain releases will move from Monday to Thursday.

  2. Fixed a bug in the test explorer and added support for testing and debugging .mbt.md files:

md.png

You can enable the following setting to allow setting breakpoints inside Markdown files: Settings > Debug: Allow Breakpoint Everywhere

setting.png

  1. moon info --package now supports fuzzy matching for package names.

2025-03-24

Β· 6 min read

Language Updates​

Pattern Matching for Bytes​

Bytes can now use array patterns for pattern matching.

fn main {
  let bytes : Bytes = "Hello, world!";
  match bytes {
    [..b"Hello", ..] => {
      println("Starts with \"Hello\"");
    }
    _ => {
      println("Doesn't start with \"Hello\"");
    }
  }
}

Char Literals as Int & Byte​

Character (Char) literals can now be used in places where an Int value is expected, with the semantic meaning being their corresponding Unicode code point. Additionally, character literals can also be used in places where a Byte value is expected, but if the Unicode code point exceeds the Byte range, an error will be raised.

fn main {
  let a : Int = 'a';
  println(a) // 97
  let b : Byte = 'a';
  println(b) // b'\x61'
  let c : Byte = 'πŸŽ‰';
  //             ^^^ Error: Char literal 'πŸŽ‰' for Byte is out of range.
}

Adjustments to Escape Sequences in String and Bytes Literals​

Due to ambiguities in interpreting escape sequences like \x.. and \o.. in different contexts (e.g., String type vs. Bytes type), we have made the following adjustments:

  • In string literals used in String-typed positions, the escape sequences \xFF and \o377 are now deprecated. It is recommended to use \u00FF or \u{FF} instead for better clarity. This change does not affect Bytes literals or string literals overloaded to Bytes, as shown in the following example:
let bytes1: Bytes = "\xFF\o377";  // ok
let bytes2 = b"\xFF\o377";        // ok, bytes2 == bytes1
let str: String = "\xFF\o377";    // warning: deprecated escape sequences
  • Support for UTF-16 surrogate pairs (e.g., \uD835\uDD04) has been removed. For characters beyond the BMP (Basic Multilingual Plane) code points, use \u{...} instead.

  • Unicode escape sequences are now deprecated in Bytes literals (b"...") and string literals overloaded to Bytes ("...").

let bytes1 = b"\u4E2D";          // deprecated, use b"\xE4\xB8\xAD" instead
let bytes2 = ("\u4E2D" : Bytes); // use ("\xE4\xB8\xAD" : Bytes) instead

Operator Overloading with Traits​

Operator overloading is no longer implemented by adding methods like op_add/op_mul/... to types. Instead, it is now implemented by implementing standard library (core) traits such as Add/Mul, etc. Below is a mapping of operators to their corresponding traits:

OperatorTrait
==Eq
+Add
-Sub
*Mul
/Div
- (prefix)Neg
%Mod
&BitAnd
|BitOr
^BitXOr
<<Shl
>>Shr

If your code has custom operator overloading, you should replace method definitions with corresponding trait impl implementations. In the future, using methods for operator overloading will trigger warnings. Once the method-based overloading is fully removed, using methods to overload operators will result in compilation errors.

If your code defines traits containing operators, you should change those trait definitions to declare super traits corresponding to the operator. For example:

trait Number {
  op_add(Self, Self) -> Self
  op_mul(Self, Self) -> Self
  literal(Int) -> Self
}

Should be changed to:

trait Number : Add + Mul {
  literal(Int) -> Self
}

Function Aliasing​

The syntax is fnalias <old_fn_name> as <new_fn_name>. Function aliases allow users to conveniently use package functions and help with refactoring when functions are moved across packages.

fnalias @hashmap.new // Equivalent to fnalias @hashmap.new as new

fn f() -> Int {
  new().length()
}

fnalias f as g

fn main {
  println("f: \{f()}")
  println("g: \{g()}")
}

fnalias also supports batch import syntax: fnalias @pkg.(f1s as g1, f2 as g2, ..)

Batch Import for Type & Trait Aliases​

  • typealias @pkg.(A, B, C) allows batch import of types.
  • traitalias @pkg.(D, E, F) allows batch import of traits.

For example, if the lib package defines two types A and B, each with a new method, another package can alias them as follows:

typealias @lib.(A, B)

fn main {
  println(A::new())
  println(B::new())
}

Removed type T for External Type Syntax​

The type T syntax for defining external types has been officially removed. External types must now be defined using extern type T. The type T syntax itself has not been removed but has gained a different meaning.

  • extern type T defines a completely external type that does not participate in MoonBit's garbage collection.
  • type T now defines a regular MoonBit type that is garbage-collected.

This new meaning of type T, combined with the newly added C FFI external object feature, enables dynamic management and release of FFI external objects.

C FFI Finalizers​

By calling moonbit_make_external_object on the C side, FFI authors can register a custom destructor function to release resources associated with an object. Here’s an example:

// MoonBit side
type Greeting // Note: not extern type

extern "c" fn Greeting::new() -> Greeting = "greeting_new"

fn main {
  ignore(Greeting::new())
}
// C side
#include "moonbit.h" // Make sure to add $MOON_HOME/include to the include paths
#include <stdlib.h>
#include <stdio.h>

char message[] = "Hello, World!";

struct greeting {
  char *data;
};

void greeting_delete(void *object) {
  fprintf(stderr, "greeting_delete\n");
  free(((struct greeting*)object)->data);
  // No need to free the object itself; MoonBit’s reference counting will handle it.
}

struct greeting *greeting_new(void) {
  char *data = malloc(sizeof(message));
  /* moonbit_make_external_object(
       void (*func_ptr)(void*),
       int32_t size
     )
     Where:
     - `func_ptr` is a function pointer responsible for releasing the resources stored in the object.
     - `size` is the size of the custom data within the object, measured in bytes.

     `moonbit_make_external_object` allocates a MoonBit object with a total size of
     `size + sizeof(func_ptr)` and returns a pointer to its data.
     `func_ptr` is stored at the end of the object,
     so the returned pointer can be directly used as a pointer to the custom data.

     If there are other C APIs that accept `struct greeting*`,
     you can directly pass a MoonBit value of type `Greeting` to them without conversion.
  */
  struct greeting *greeting =
      moonbit_make_external_object(&greeting_delete, sizeof(struct greeting));
  greeting->data = data;
  return greeting;
}

LLVM Debugging​

The LLVM backend has initially implemented the ability to print local variable values in the debugger. Developers using gdb, lldb, and other debugging tools can now view local variable values for basic data types (integers, floating-point numbers). Support for strings, arrays, and complex data structures is actively in development.

Build System Updates​

  • The bleeding-edge Windows toolchain now supports the LLVM backend. Windows users can install it as follows:
$env:MOONBIT_INSTALL_VERSION = "bleeding"; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex
  • A dev version of the toolchain is now available, which retains more debugging information for diagnosing compiler issues and errors. Install it using:
# Unix (Linux or macOS)
curl https://cli.moonbitlang.com/install/unix.sh | MOONBIT_INSTALL_DEV=1 bash
# Windows (PowerShell)
$env:MOONBIT_INSTALL_DEV = 1; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex
# moon
moon upgrade --dev

Note: The dev toolchain does not support the LLVM backend yet.

  • MoonBit-supported Markdown files can now be tested. These test code snippets will run as black-box tests:
moon test --md

IDE Updates​

  • IDE now supports syntax highlighting for mbti files. highlight.png

  • Codelens in the IDE now includes emojis. emoji.png

  • Markdown files with a *.mbt.md suffix now enable MoonBit LSP support (including error messages, completion, etc.).

2025-03-10

Β· 4 min read

Language Updates​

Pattern Guards​

Pattern guards can be specified by appending if ... to a pattern. A branch with a pattern guard will only execute if the matched value satisfies the pattern and the guard condition evaluates to true. If the guard evaluates to false, the match will continue to the next applicable branch.

This feature simplifies code using pattern matching. For example, simplifying arithmetic expressions:

fn simplify(e : Expr) -> Expr {
  match e {
    Add(e1, Lit(0)) => e1
    Sub(e1, e2) if e1 == e2 => Lit(0)
    //          ^^^^^^^^^^^ pattern guard
    _ => e
  }
}

Pattern guards can also be used with the is expression to introduce new variables. For example:

fn json_get_key(json : Json, key : String) -> Json! {
  match json {
    Object(map) if map[key] is Some(value) => value
    _ => fail!("key not found: \{key}")
  }
}

Attribute Syntax​

Attribute syntax is now supported as a replacement for the old @alert deprecated "message" pragmas. Each attribute must be on a separate line, and line breaks are not allowed within attributes.

Supported attributes:

  1. #deprecated("message") – Marks a function as deprecated and displays a message when used.
  2. #coverage.skip – Excludes a function from coverage testing.

The old pragma syntax will be removed in the future.

#deprecated("use function g instead")
#coverage.skip
fn f() -> Unit {
  ...
}

Bytes Type Supports String Literals for Initialization and Assignment​

The Bytes type now supports initialization and assignment using string literals. The string is stored as UTF-8 encoded Bytes. For example:

fn main {
  let xs : Bytes = "123"
  let ys : Bytes = "δ½ ε₯½οΌŒδΈ–η•Œ"
}

enum Supports Custom Tag Values​

Enums now support custom tag values, which is useful when interfacing with native C FFI. For example, using the open syscall:

enum OpenFlag {
  O_RDONLY = 0x00
  O_WRONLY = 0x01
  O_RDWR   = 0x02
}

extern "c" fn open(path : Bytes, flag : OpenFlag) -> Int = "open"

test {
  let path : Bytes = "tmp.txt"
  let fd = open(path, O_RDONLY)
}

Enhanced const Declarations​

const now supports:

  • Referencing other constants.
  • Arithmetic, bitwise, and comparison operations on built-in types.

Example:

const A : Int = 1 + 2 * 3
const B : Int = A * 6

Deprecating Implicit Trait Implementation via Methods​

Implicit trait implementation via methods is now deprecated. To implement a trait for a type, an explicit impl block must be used.

// Implicitly implementing Show for T (deprecated)
fn T::output(self : T, logger : &Logger) -> Unit {
  ...
}

// You should migrate to explicit implementation
impl Show for T with output(Self : T, logger : &Logger) -> Unit {
  ...
}

Removed Direct Invocation of fn T::f(..)​

Direct invocation of fn T::f(..) has been removed, following prior deprecation warnings. In the future:

  • Methods defined as fn f(self : T, ..) can be used as normal functions.
  • Methods written as fn T::f(..) must be called using T::f(..) or x.f(..) syntax.

For more details on the new semantics, see GitHub PR #1472.

Splitting Part of the Native Backend Runtime​

A portion of the Native backend runtime has been moved into a separate C file located at $MOON_HOME/lib/runtime.c. If you use a custom build process, such as compiling the C file manually, ensure this file is included during compilation.

LLVM Backend Released for Bleeding-Edge Toolchain​

The LLVM backend is now available in the bleeding-edge toolchain. Currently, it supports x86_64 Linux and ARM64 macOS. To install the bleeding-edge version of MoonBit, run:

curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s 'bleeding'

You can then enable the LLVM backend during build, test, and run phases by passing the --target llvm option. Example:

moon build --target llvm

Build System Updates​

  1. Added moon check --explain, which provides detailed information about error codes. explain

  2. Added a "native-stub" configuration in moon.pkg.json to specify C stub files used in a package. These stubs are built and linked automatically during compilation. If your project previously included C stub files in cc-flags, you can now declare these C stub files in the "native-stub" field.

  3. Relaxed the moon publish requirement to pass moon check. If check fails, users will be prompted whether they want to proceed with publishing.

IDE Updates​

  1. Embedded MoonBit code blocks in Markdown now support formatting. Users can format MoonBit code inside Markdown using the MoonBit plugin.

    fmt

2025-02-24

Β· 4 min read

Language Update​

Enhancements to String Pattern Matching​

a. Support for String Literals in Array Patterns

You can now use the .. operator to match a substring within an array pattern of type String:

let str = "https://try.moonbitlang.com"
match str {
  [.."https://", ..path] => println(path)
  [.."http://", ..path] => println(path)
  _ => println("unexpected protocol")
}

This is a simplified version of the following code:

 let str = "https://try.moonbitlang.com"
 match str {
   ['h', 't', 't', 'p', 's', ':', '/', '/', .. path] => println(path)
   ['h', 't', 't', 'p', ':', '/', '/', .. path] => println(path)
   _ => println("unexpected protocol")
 }

b. Support for Using const Constants in Array Patterns

const HTTP = "http://"
const HTTPS = "https://"

///|
fn main {
  let str = "https://try.moonbitlang.com"
  match str {
    [..HTTP, .. path] => println(path)
    [..HTTPS, .. path] => println(path)
    _ => println("unexpected protocol")
  }
}

Support for is Expression in while Conditions​

fn main {
  let queue = @queue.of([1,2,3])
  while queue.pop() is Some(n) {
    println(n)
  }
}
// Output:
// 1
// 2
// 3

Support for String Type in Array Literals​

Array literals now support the String type.

Breaking Change in C FFI Handling​

[BREAKING CHANGE] In C FFI, all MoonBit objects now point to the first field instead of the object header. In the native MoonBit backend, each object has an object header (defined in moonbit.h) to support garbage collection. Previously, MoonBit objects in the native backend were pointers to the object header, meaning C wrapper functions were required to skip over the header to access actual data when binding MoonBit to C FFI. To improve the C FFI experience, we now have MoonBit objects pointing directly to the first field of the object instead. This eliminates the need to explicitly handle object headers in C FFI bindings. For example, Bytes can now directly represent const char* or void*, and FixedArray[T] can represent T* without needing C wrappers.

However, this change is a breaking change for C FFI. Existing C wrappers that skip object headers will need to be updated. Libraries or programs using C FFI should be modified after this compiler update.

New FuncRef[T] Type for FFI​

A new special type FuncRef[T] has been added in moonbitlang/builtin, representing a non-capturing function of type T. This type is used for FFI callbacks, such as Unix signal handlers. You create a FuncRef[T] by writing an uncaptured anonymous function where a FuncRef is expected. FuncRef[T] cannot be called directly, as it is intended for FFI binding.

External Type Syntax Update​

The syntax for external types used in FFI has changed to extern type T. Previously, external types were defined using type T. Going forward, we will use the more explicit extern type T syntax for external types. The old type T syntax is deprecated, and the compiler will issue a warning. You can automatically complete this syntax migration using moon fmt.

Once the old type T syntax is completely removed, we will use it for other purposes. extern type T refers to an entirely external object, and the MoonBit garbage collector will not manage it. In the future, type T will represent externally sourced objects with standard MoonBit object structures, subject to garbage collection, such as FFI objects with custom finalizers.

Removal of Old Trait Object Syntax​

The old trait object syntax has been removed. The compiler had previously issued a warning for the old trait object syntax, and now the old Trait syntax has been removed. Trait object types should now be written as &Trait.

Build System Updates​

  • moon info now supports the --package argument to specify which package to process.

  • The moon.pkg.json configuration file now includes a supported-targets field to specify the backends supported by the current package (defaults to all backends if not set). If you try to build the package in an unsupported backend or the backend of a dependency is incompatible, the build system will raise an error.

  • The moon.pkg.json file now includes a native-stub field to declare .c files that need to be built and linked alongside the package.

IDE Updates​

tourcn.png

  • Language services now support processing MoonBit code within markdown. To enable this, add the following configuration at the beginning of the markdown:
---
moonbit: true
---

2025-02-10

Β· 3 min read

Language Updates​

New is Expression​

  1. The syntax for this expression is expr is pat. The expression is a Bool type and evaluates to true when expr matches the pattern pat. For example:
fn use_is_expr(x: Int?) -> Unit {
  if x is Some(i) && i >= 10 { ... }
}
  1. The pattern can introduce new binders, which can be used in the following cases:
  • In e1 && e2, if e1 is an is expression, the binders introduced by the pattern can be used in e2.
  • In if e1 && e2 && ... { if_branch } else { ... }, binders introduced by is expressions in the &&-chained conditions like e1 & e2 can be used in the if_branch.

String Construction and Pattern Matching​

  1. New support for constructing strings using array spread syntax, for example:
fn string_spread() -> Unit {
  let s = "helloπŸ€£πŸ˜‚πŸ˜"
  let sv = s[1:6]
  let str : String = ['x', ..s, ..sv, '😭']
  println(str) // xhelloπŸ€£πŸ˜‚πŸ˜ello🀣😭
}

In an array spread, individual elements are Char values. You can use .. to insert a String or a @string.View segment. This syntax is equivalent to using StringBuilder to construct a string.

  1. Support for pattern matching on strings using array patterns, which can be mixed with array literal patterns, for example:
fn match_str(s: String) -> Unit {
  match s {
    "hello" => ... // string literal pattern
    [ 'a' ..= 'z', .. ] => ... // array pattern
    [ .., '😭' ] => ... // array pattern with unicode
    _ => ...
  }
}

New Compiler Warnings​

  • The compiler now warns about unused guard statements and missing cases in guard let ... else ....
fn main {
  guard let (a, b) = (1, 2)
  ^^^^^^^^^ ----- useless guard let
  println(a + b)
}

moonfmt Fixes​

  • Fixed formatting errors related to async code in moonfmt.
  • Adjusted insertion rules for ///| markers.

Package Updates​

  • moonbitlang/x/sys now supports the native backend and fixes inconsistencies across different operating systems.

  • The fs package in moonbitlang/x has been updated with improved error handling.

  • String-related operations are being reorganized. The string package will provide more Unicode-safe APIs while deprecating some APIs that expose UTF-16 implementation details. During this transition, string methods may become unstable. It is recommended to use iter methods or pattern matching to access string elements.

  • Refactored ArrayView/StringView/BytesView types by moving them from the @builtin package to their respective type-related packages. Their names have been updated accordingly to @array.View/@string.View/@bytes.View.

IDE Updates​

  • Added code action support for filling in missing cases in pattern matching.

  • Enabled inline autocompletion for all cases in empty pattern matches.

  • Fixed a bug in trait method "Go to Reference".

  • Fixed missing autocompletion for variables introduced in guard let ... else ... and improved pattern completion in else branches.

Build System Updates​

  • Fixed a bug in moon test where panic tests were being skipped on the native backend.

Documentation Updates​

2025-01-13

Β· 3 min read

Language Updates​

  • Experimental Async Support

    Experimental support for asynchronous programming has been added. You can declare asynchronous functions using async fn ... and call them with f!!(...). MoonBit also provides primitives for interrupting control flow. For details, see docs/async-experimental.

    Currently, the async standard library and event loop are under development. Using the JavaScript backend's event loop and Promise API is easier for asynchronous programming. As this feature is experimental, breaking changes may occur based on feedback. We welcome and appreciate your testing and feedback.

  • Upcoming Changes to Method Semantics

    Later this week, we will make major changes to simplify method-related rules. Currently:

    • Methods can be declared as fn f(self: T, ..) or fn T::f(..).

    • Methods with Self as the first parameter can be called with xx.f(..).

    • If unambiguous, methods can also be called with f(..).

    However, the last rule lacks user control and consistency between call syntax (f(..)) and declaration syntax (T::f(..)). The new design will:

    • Allow fn f(self: T, ..) to define methods callable with xx.f(..) or f(..). These methods share the same namespace as regular functions and cannot have duplicate names.

    • Allow fn T::f(..) to define methods callable with xx.f(..) or T::f(..). These methods cannot be called as regular functions (f(..)).

    Intuition: All fn f(..) definitions are regular functions, while fn T::f(..) definitions are placed in a small namespace associated with T.

    For methods like new (without Self as the first parameter), use fn new(..) to enable direct calls as new(..). Library authors are encouraged to:

    • Use fn f(..) for unambiguous functions and make the first parameter self for xx.f(..) calls.

    • Use fn T::f(..) for potentially ambiguous functions to place them in a scoped namespace.

  • Enhanced Alerts

    • Trigger alerts on type usage.

    • Trigger alerts on specific constructor usage.

  • Improvements to ArrayView/BytesView/StringView:

  1. Support for negative indices in view operations, e.g.:
let arr = [1, 2, 3, 4, 5]
let arr_view = arr[-4:-1]
println(arr_view) // [2, 3, 4]
  1. Support for pattern matching in BytesView with byte literals, e.g.:
fn f(bs: BytesView) -> Option[(Byte, BytesView)] {
  match bs {
    [b'a'..=b'z' as b, ..bs] => Some((b, bs)),
    _ => None
  }
}
let s = b"hello"[:]
let r = f(s)
println(r) // Some((b'\x68', b"\x65\x6c\x6c\x6f"))
  1. The as keyword in array patterns is now optional. [a, ..rest, b] is valid, and the formatter will automatically omit as from [a, ..as rest, b].

IDE Updates​

  • Added a command to toggle multi-line strings.
  • Added more detailed information to workspace symbols, making it easier to search for specific functions and types.

Build System Updates​

  • Fixed a bug in doc tests affecting multi-line string test results.

Documentation Updates​

  • MoonBit Tour now supports debug codelens with value tracking enabled by default.

  • Launch Moonbit Online Judge, a platform for users to learn MoonBit by solving problems.

oj.png

Standard Library Updates​

  • Updated the behavior of Int64 to JSON conversions. Precision is no longer lost through Double conversion; values are now preserved as strings.