Skip to main content

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

· 7 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.

2024-12-30

· 4 min read

Language Update

  • Added labeled loop syntax, which allows you to jump directly to a specified layer in a multi-layer loop, using ~ as the suffix for the label.
fn f[A](xs : ArrayView[A], ys : Iter[Int]) -> @immut/list.T[(Int, A)] {
l1~: loop 0, xs, @immut/list.Nil {
_, [], acc => acc
i, [x, .. as rest], acc =>
for j in ys {
if j == i {
continue l1~ i + 1, rest, @immut/list.Cons((j, x), acc)
}
if j + i == 7 {
break l1~ acc
}
} else {
continue i - 1, rest, acc
}
}
}
  • New Discard Argument, function arguments named with a single underscore will be discarded, multiple arguments can be discarded within the same function.
fn positional(a : Int, b : Int) -> Int {
a + b
}

fn discard_positional(_: Int, _: Int) -> Int {
1
}
  • The semantics of pub have officially changed from being fully public to read-only, and the pub(readonly) syntax has been deprecated. To declare a fully public type/struct/enum, you should now use pub(all). To declare a fully public (externally implementable) trait, use pub(open). This change was previously announced via a warning, and if you already followed the warning and updated pub to pub(all) or pub(open), you only need to replace pub(readonly) with pub to complete the migration. The moon fmt tool can automatically convert pub(readonly) to pub.

  • In the future, the behavior where a trait will fallback to method implementation if no explicit implementation is found may be removed. We encourage new code to use the explicit impl Trait for Type syntax instead of relying on methods for trait implementation. In cases of no ambiguity, you can still use dot syntax (impl Trait for Type with method(...)), so adopting explicit impl syntax won't sacrifice convenience.

  • The old prefix syntax for labeled parameters has been removed.

  • The old syntax for accessing newtype contents using .0 has been removed. Additionally, if a newtype contains a tuple, index access like .0, .1, etc., will now automatically forward to the tuple inside the newtype. For example:

type Tuple (Int, String)

fn main {
let t = (4, "2")
println(t.0) // 4
println(t.1) // 2
}
  • Parameters have been added to derive(FromJson) and derive(ToJson) to control the specific behavior and data layout of type serialization and deserialization. The detailed changes can be found in Docs > Language > Deriving.

    You can now rename fields and adjust the serialization format of enums. The specific behavior of JSON serialization and deserialization may be optimized and changed in the future.

enum UntaggedEnum {
C1(Int)
C2(String)
} derive(ToJson(repr(untagged)), Show)
// { "0": 123 }, { "0": "str" }

enum InternallyTaggedEnum {
C1(Int)
C2(String)
} derive(ToJson(repr(tag="t")), Show)
// { "t": "C1", "0": 123 }

enum ExternallyTaggedEnum {
C1(Int)
C2(String)
} derive(ToJson(repr(ext_tagged)), Show)
// { "C1": { "0": 123 } }

enum AdjacentlyTaggedEnum {
C1(Int)
C2(String)
} derive(ToJson(repr(tag="t", contents="c")), Show)
// { "t": "C1", "c": { "0": 123 } }

struct FieldRenameAllCamel {
my_field : Int
} derive(ToJson(rename_all="camelCase", repr(tag="t")), Show)
// { "t": "fieldRenameAllCamel", "myField": 42 }

struct FieldRenameAllScreamingSnake {
my_field : Int
} derive(ToJson(rename_all="SCREAMING_SNAKE_CASE", repr(tag="t")), Show)
// { "t": "FIELD_RENAME_ALL_SCREAMING_SNAKE", "MY_FIELD": 42 }

IDE Update

  • IDE now supports gotodef/gotoref/rename for loop label.

  • Document symbols are now displayed in a hierarchical way in the IDE, as follows. layer.png

  • Fixed bug in white box testing regarding type renaming.

  • Added IDE support for automatically omitting parameter inlay hints in case of repetition.

  • IDE adds value tracing. Clicking on "Trace" codelen above the main function will enable it, and clicking again will disable it.

    • For variables inside a loop, only the latest value and the hit count are shown. trace.png

Build System Update

  • moon adds a --no-strip parameter to preserve symbol information in release build mode.

Document Update

  • Fixed an issue where MoonBit Tour's theme was not maintained when switching pages.

  • Added range, range pattern and newtype sections to MoonBit Tour.

2024-12-16

· 3 min read

Language Update

  • New Trait object syntax: &Trait

Trait object syntax has been changed from writing Trait directly to &Trait (the old syntax has been deprecated). We made this change to separate trait object type and the trait itself syntactically and avoid confusion. All places that mention trait objects, including type annotation, defiing method on trait objects (fn &Trait::method(...)) and trait object creation (... as &Trait) should be modified to adapt to this change.

  • New Local Types Language Feature

It’s now possible to declare types (structs, enums, newtypes) that are only visible within the scope of the current top-level function. You can also use derive to add new methods to these local types. For example:

fn toplevel[T: Show](x: T) -> Unit {
enum LocalEnum {
A(T)
B(Int)
} derive(Show)
struct LocalStruct {
a: (String, T)
} derive(Show)
type LocalNewtype T derive(Show)
...
}

Note that local types can use the generic parameters of the current top-level function but cannot introduce additional generics of their own. Local types can use derive to generate associated methods, but they cannot define other new methods. Also, declaring an error type within a local type is not yet supported.

IDE Update

  • Fixed some LSP related bugs.

    • Fixed the bug that when hovering, the type of the payload and the type of the error type are linked together.
    • Fixed a bug where LSP would not serve a single file after adding it to a module (creating moon.pkg.json in the same level folder) and then removing it (deleting moon.pkg.json).
    • Fixed test-import-all configurations that were broken sometimes.
    • Fix a bug in LSP's strange inlay hint.
  • Enable block-line option for LSP formatter.

  • LSP supports warn-list configuration.

  • Optimise the debug experience in web IDE. When user opens devtools and clicks debug, it will directly stop at the main function.

debug.gif

  • Allow test wrapping in doctest, support for updating inspect and panic test.
/// ```
/// test "panic test xxx" {
/// panic()
/// }
/// ```
  • MoonBit AI Feature

The AI now supports model switching, interruption, and retrying while generating.

ai.gif

Build System Update

  • moon run runs a test with the support of passing --stack-size directly to adjust the v8 stack size.

  • Breaking Change

During black-box testing, the public definitions from the tested package will be automatically imported. For example, when testing the @json package, functions or types from @json can be used without prefixing @json. To disable this behavior, you must specify "test-import-all": false in the moon.pkg.json file.

Document Update

  • MoonBit Tour

The tutorials on Web IDE have been migrated to the new tour, with changes and additions made accordingly. The code repo is here.

2024-12-02

· 3 min read

Language Updates

  • Added support for range patterns to match a range of integer or character values in pattern matching.

The syntax for range patterns is a..<b (excluding the upper bound b) or a..=b (including the upper bound b). Bounds can be literals, constants declared with const, or _, which indicates no constraint on that side:

const Zero = 0
fn sign(x : Int) -> Int {
match x {
_..<Zero => -1
Zero => 0
Zero..<_ => 1
}
}

fn classify_char(c : Char) -> String {
match c {
'a'..='z' => "lowercase"
'A'..='Z' => "uppercase"
'0'..='9' => "digit"
_ => "other"
}
}
  • Support calling trait with x.f(...)
trait I {
f(Self) -> Unit
}

type MyType Int
impl I for MyType with f(self) { println(self._) }

fn main {
let my : MyType = 42
my.f()// Output 42
}

If x has type T, the x.f(...) syntax resolves as follows:

  1. If T defines a method f, call T::f.
  2. If a trait does impl SomeTrait for T with f in the same package as T, call SomeTrait::f. If multiple f are found, the compiler reports an ambiguity error.
  3. If neither of the above applies, the compiler searches for impl SomeTrait for T with f within the current package. Notice that this rule only work in current package. If T is defined outside the current package, local implementation for T cannot be called with dot syntax outside.

These rules enhance the flexibility of MoonBit’s dot syntax while maintaining semantic clarity and strong refactor safety. Resolution of x.f(...) only involves the package defining T and the current package, avoiding issues caused by newly introduced dependencies.

  • Added support for trait alias

No new syntax is introduced. Trait aliases use the same syntax as type aliases:

typealias MyShow = Show
impl MyShow for MyType with ...

IDE Updates

  • Web IDE now supports updating inspect tests. try-inspect.gif

  • Generated code can be revised in MoonBit AI before inserting, and the revised code is automatically checked for syntax and type correctness. ai-modify.gif

  • Fixed syntax highlighting issues for Markdown code blocks in documentation comments.

Build System Updates

  • moon check now supports passing a warn list.

  • moon test can run tests embedded in project documentation.

    • Usage: moon test --doc runs all tests within documentation comments of the current project.
    • Note: Tests must be enclosed between lines with ```. For example: moon-test.png
  • Fixed an issue with local functions and return expressions in moon fmt.

  • Fixed an issue in moon fmt which comments near array indexing syntax are misplaced during formatting.

  • moon fmt enabled -block-style by default.

Docs Update

2024-11-18

· 4 min read

Language Updates

  • Trait now has abstract and pub(readonly) visibility.
  1. An abstract/readonly trait behaves the same as a fully public trait within the current package.
  2. Outside the current package, you can't call methods in an abstract trait directly, and you can't write new implementations of an abstract trait.
  3. A pub(readonly) trait cannot be implemented outside the current package, but methods can be called directly.
  4. The default visibility for a trait has been changed from private to abstract. To explicitly define a private trait, use the priv trait keyword.
  5. When implementing an abstract or readonly trait, at least one method must be implemented using the impl Trait for Type syntax to allow external usage of that implementation.

Example of abstract trait:

trait Number {
add(Self, Self) -> Self
}

pub fn add[N : Number](x : N, y : N) -> N {
Number::add(x, y)
}

impl Number for Int with add(x, y) { x + y }
impl Number for Double with add(x, y) { x + y}

Using the moon info command to generate a .mbti interface file, the exported interface is as follows:

trait Number
impl Number for Int
impl Number for Double

fn add[N : Number](N, N) -> N

Externally, the add function can only be called for Int and Double. The compiler ensures that the Number trait can only be implemented for Int and Double.

  • Visibility Adjustments for Types and Traits

To encourage the use of readonly types/traits to provide more robust APIs, we plan to change the default visibility for type and trait definitions to readonly in the future.

To declare fully public visibility:

  1. Types: Use pub(all) struct/enum/type.
  2. Traits: Use pub(open) trait to allow external implementations.

Currently:

  1. pub still means fully public visibility, but the compiler will issue a warning recommending migration to pub(all) or pub(open).
  2. The moon fmt formatter will automatically migrate existing pub to pub(all) or pub(open).
  • New Private Fields in struct Types:

Public structs can now have private fields by adding the priv keyword before specific fields.

  1. External Visibility: Private fields are completely hidden from the outside world. They cannot be read or modified externally.
  2. If a struct contains private fields, it cannot be directly constructed using literals from outside the module. ****However, the struct update syntax ({ ..old_struct, new_field: ... }) can be used to update public fields while maintaining the private field's state.
  • New suffix-style label argument syntax

The new syntax is equivalent to moving ~ after the identifier, and the ~ can be omitted in the case of forwarded options of the form ~label?

enum Foo {
Bar(label~ : Int, Bool)
}
fn f(a~ : Int, b~ : Int = 0, c? : Int) -> Unit {...}
fn main {
f(a=1, b=2, c=3)
f(a=1, c?=Some(3))// b Default values are declared and can be omitted. c Forwarding option Syntax as usual
let a = 1
let c = Some(3)
f(a~, c?) // Like at the declaration, punning takes the form of a suffix
}

The old syntax is deprecated:

enum Foo {
//deprecated
Bar(~label : Int, Bool)
}
//deprecated
fn f(~a : Int, ~b : Int = 0, ~c? : Int) -> Unit {}
fn main {
let a = 1
let c = Some(3)
f(~a, ~c?) //deprecated
}

You can migrate code to the new syntax with moon fmt.

  • Reserved Keywords

A series of reserved keywords have been added for future use by the compiler. Currently:

  1. Using these reserved keywords will produce a warning, advising users to migrate.
  2. These keywords may become language keywords in future versions.

Reserved keywords are:

module
move
ref
static
super
unsafe
use
where
async
await
dyn
abstract
do
override
typeof
virtual
yield
local
method
alias
  • Planned Changes to Bytes Type

The standard library's Bytes type will be changed to an immutable type. Therefore, functions that modify Bytes will be marked as deprecated.

Alternatives for mutable byte operations:

  1. Array[Byte]
  2. FixedArray[Byte]
  3. Buffer
  • Rename of @test.is

The @test.is function has been renamed to @test.same_object. @test.is is now deprecated. The is keyword will become a reserved word in the future.

IDE Updates

  • Fixed an issue where moon fmt --block-style didn't handle document comments like ////| comment correctly.

  • IDE adapted suffix label argument syntax to support gotoref and rename.

  • Support code highlighting in documentation.

  • Fixed an issue in LSP with @ autocompletion when there are internal packages.

Build system update

  • The -verbose option now outputs the currently running command.

MoonBit ProtoBuf