Skip to main content

20251202 MoonBit Monthly Update Vol.06

· 9 min read

Version 0.6.33

Language Update

  • Improved functionality for ReadOnlyArray.

    ReadOnlyArray was introduced in the previous version, primarily used for declaring lookup tables, and the compiler performs more performance optimizations for ReadOnlyArray. In this version, the feature support for ReadOnlyArray has been improved to provide a user experience basically consistent with other array types, such as pattern matching, slicing, and spread operations.

    fn main {
      let xs: ReadOnlyArray[Int] = [1,2,3]
      let _ = xs[1:]
      match xs {
        [1, .. rest] => ...
        ...
      }
      let _ = [..xs, 1]
    }
  • bitstring pattern supports signed extraction, allowing the extracted bits to be interpreted as signed integers, for example:

    fn main {
    let xs : FixedArray[Byte] = [0x80, 0, 0, 0]
    match xs {
      [i8be(i), ..] => println(i) // prints -128 because 0x80 is treated as signed 8-bit int
      _ => println("error")
    }
    }
  • Cascade function call improvements

    Previously, in cascade-style function calls like x..f()..g(), the return type of f was required to be Unit. This restriction has now been removed. When f returns a value of a type other than Unit, an invalid_cascade warning will be triggered, and at runtime this return value will be implicitly discarded:

    struct Pos {}
    fn Pos::f(_ : Self) -> Int  { 100 }
    fn Pos::g(_ : Self) -> Unit { ()  }
    fn main {
      let self = Pos::{}
      self
      ..f() // warning, discard the returned value 100
      ..g()
    }

    If you wish to prohibit such implicit discarding in your project, you can treat this warning as an error by configuring "warn-list": "@invalid_cascade".

  • Syntax parsing improvements

    • Improved error recovery when :: is omitted in StructName::{ field1: value }
    • Improved error recovery when range syntax is incorrectly written in for x in a..=b {} and match e1 { a..=b => e2 }
  • Improvements to .mbt.md code block support We have decided to make markdown code blocks that participate in compilation more explicit. The specific changes are as follows:

    • Code blocks marked only as mbt or moonbit will no longer be compiled. These code blocks need to be explicitly marked as check, i.e., mbt check or moonbit check, to be compiled as before.

    • Added mbt test and mbt test(async) code blocks. In addition to participating in compilation, these code blocks will also wrap the code in a test or async test block. When using these two types of code blocks in markdown, users no longer need to manually write test {} or async test.

          # A Markdown Example
      
          Highlighting only:
      
          ```mbt
          fn f() -> Unit
          ```
      
          Highlighting and checking:
      
          ```mbt check
          fn f() -> Unit {...}
          ```
      
          Highlighting, checking and treated as a test block:
      
          ```mbt test
          inspect(100)
          inspect(true)
          ```

    The markdown in docstrings has also undergone the same changes, although mbt check is not currently supported but will be supported in the future.

  • #label_migration attribute

    The #label_migration attribute supports declaring aliases for parameter labels, with two main uses: first, it allows giving the same parameter two different labels; second, when an additional msg is provided, it can be used to deprecate a certain parameter label:

    #label_migration(x, alias=xx)
    #label_migration(y, alias=yy, msg="deprecate yy label")
    fn f(x~ : Int, y? : Int = 42) -> Unit { ... }
    
    ///|
    fn main {
      f(x=1, y=2)   // ok
      f(xx=1, yy=2) // warning: deprecate yy label
      f(x=1)        // ok
    }
  • #deprecated default behavior improvement

    The default behavior of deprecated has been changed to skip_current_package=false, meaning warnings will also be reported for usage within the current package. If unexpected warnings appear in recursive definitions or tests, you can explicitly disable warnings for the current package using #deprecated(skip_current_package=true), or use the newly added #warnings attribute to temporarily disable warnings.

  • Warnings and alerts improvements

    • Added mnemonics for warnings

      Now you can configure warnings by their names instead of numbers: "warn-list": "-unused_value-partial_match"

    • #warnings attribute support

      Now supports locally enabling/disabling warnings through the #warnings attribute. The parameter inside the attribute is the same string format as the warn-list configuration. The string contains multiple warning names, each preceded by a symbol indicating the configuration for that warning: -name means disable the warning; +name means enable the warning; @name means if the warning is already enabled, escalate it to an error.

      For example, the following example disables the unused_value warning for the entire function f and escalates the default-enabled deprecated warning to an error. Now it won't prompt that the variable is unused, but if f uses a deprecated API, compilation will fail:

      #warnings("-unused_value@deprecated")
      fn f() -> Unit {
        let x = 10
      }

      Currently, this attribute only supports configuring certain specific warnings.

    • Merge alerts and warnings

      Deprecate alerts-related configurations. Now alerts have become a subset of warnings. When using -a to disable all warnings, all alerts will also be disabled. Specifically, in warn-list, you can use alert to refer to all alerts, and alert_<category> to refer to a specific category of alerts:

      #warnings("-alert")
      fn f() -> Unit { ... } //Disable all alert warnings
      
      #warnings("@alert_experimental")
      fn g() -> Unit { ... } //Related to APIs marked with #internal(experimental, "...")
    • test_unqualified_package warning

      Added the test_unqualified_package warning, which is disabled by default. When enabled, it requires blackbox tests to reference the tested package's API using the @pkg.name format, otherwise this warning will be triggered.

  • Lexmatch syntax update

    The experimental lexmatch expression now supports the first (default) matching strategy. Under this matching strategy, search mode and non-greedy quantifiers are supported. For specific details, please refer to the proposal.

    // Find the first block comment and print its content
    lexmatch s { //  `with first` can be omitted
      (_, "/\*" (".*?" as content) "\*/", _) => println(content)
      _ => ()
    }
  • Type inference improvements

    Fixed the issue where type information in X could not propagate to expressions when the expected type is ArrayView[X], and the issue where type parameters could not propagate to expressions when the expected type is a newtype with parameters.

  • Added #module attribute for importing third-party JS modules For example, the following code imports the third-party JS module "path" and uses "dirname" from that module

#module("path")
extern "js" fn dirname(p : String) -> String = "dirname"

The code above will result in the following generated JS code (simplified, the actual code has name mangling):

import { dirname } from "path";

Toolchain Update

  • IDE completion improvements

    The IDE now displays deprecated APIs with strikethrough formatting: alt text

  • Build system improvements

    • Enhanced the readability of expect/snapshot test diffs. Now, these areas use the unified diff format to display the differences between expected and actual values, making them readable even in scenarios where colors are not output, such as in CI and files, while still showing colored and highlighted comparison results when color display is available. alt text
  • New Build system backend

    We have basically completed a complete rewrite of the build system backend, which can be enabled for trial use through the NEW_MOON=1 environment variable.

    The new backend is less prone to errors due to various edge cases during the build process, offering stronger stability, while also providing performance improvements compared to the current implementation. The new backend now supports the vast majority of existing features and behaves exactly the same as the current implementation, but may lack some optimizations in certain situations (such as running tests in parallel).

    If you encounter any issues when running the new backend, including performance issues and behavioral inconsistencies, please report them at https://github.com/moonbitlang/moon/issues.

  • File path-based filtering for moon {build,check,info} moonbuild#1168.

    When running moon build, moon check, or moon info, you can pass in the folder path where the package to be processed is located or a file path within it, and only the corresponding command for that package will be executed. This usage is similar to -p <package name>, but doesn't require entering the complete package name. This feature cannot be used simultaneously with -p. For example:

    # Build only the package at path/to/package
    moon build path/to/package
    
    # Check only the file at path/to
    moon check path/to/file.mbt
  • moon doc symbol lookup We have created a symbol search command-line tool similar to go doc, making it convenient for AI agents or developers to quickly search for available APIs. Currently supports the following features:

    • Query available packages in a module
    • Query all available items (values, types, traits) in a package
    • Query members of a type (method, enum variant, struct field, trait method)
    • Follow aliases to their final definition when querying
    • Query built-in types
    • Support glob patterns

    Simply run moon doc <symbol name or package name> to query the documentation for the corresponding symbol or package.

  • moon fmt improvements

    • Support formatting code blocks marked as moonbit or moonbit test in documentation comments
    • moon.pkg.json supports configuring ignore lists
    { // in moon.pkg.json
      "formatter": {
        "ignore": [
          "source1.mbt",
          "source2.mbt"
        ]
      }
    }
  • async test now supports limiting the maximum number of concurrently running tests, with a default value of 10, which can be modified through "max-concurrent-tests": <number> in moon.pkg.json

Standard Library and Experimental Library Update

  • Deprecate Container::of function

    Now ArrayView is the unified immutable slice that can be created from Array, FixedArray, and ReadonlyArray. Therefore, initialization functions like of and from_array are unified as Type::from_array, with the parameter changed from accepting Array to ArrayView. It is now recommended to use Type::from_array to create containers from array literals.

  • Added MutArrayView as the unified mutable slice

    ArrayView changed from a mutable type to an immutable type in the previous version, but sometimes it's necessary to modify elements of the original array through a slice, so MutArrayView was introduced as a complement.

    MutArrayView can be created from Array and FixedArray.

  • Renamed @test.T to @test.Test and @priority_queue.T to @priorityqueue.PriorityQueue

  • String indexing improvements

    string[x] will now return UInt16. Please migrate using code_unit_at

  • moonbitlang/x/path experimental library improvements

    Supports dynamic switching between Windows path and POSIX path processing, with Python os.path style API design.

  • moonbitlang/async updates

    • moonbitlang/async experimentally supports the js backend. Currently covered features include:

      • All features unrelated to IO, including TaskGroup, control flow constructs like @async.with_timeout, async queues, etc.

      • Provides a package for JS interaction called moonbitlang/async/js_async, supporting bidirectional conversion between MoonBit async functions and JavaScript Promises, and supporting automatic cancellation handling based on AbortSignal

    • Added WebSocket support, which can be imported through the moonbitlang/async/websocket package

    • moonbitlang/async/aqueue now supports fixed-length async queues. When the queue is full, it supports three different behaviors: blocking the writer/overwriting the oldest element/discarding the newest element, which can be controlled by parameters when creating the queue

    • The HTTP client and HTTP request API in moonbitlang/async/http now support specifying HTTP CONNECT proxies. It can support HTTPS proxies with full-process encryption and proxies that require authentication

    • Improved the HTTP server API, now user callback functions can process one request at a time without manually managing connections

20251103 MoonBit Monthly Update Vol.05

· 7 min read

Version v0.6.30+07d9d2445

Compiler Updates

1. Updates of the alias system

MoonBit has three different alias syntaxes for traits, functions, and types respectively:

  • traitalias @pkg.Trait as MyTrait
  • fnalias @pkg.fn as my_fn
  • typealias @pkg.Type as MyType

This distinction introduces unnecessary complexity and limitations. For example, we couldn't create aliases for const values. In upcoming versions, we will introduce a new using syntax to unify alias creation.

The using { ... } syntax unifies traitalias, fnalias, and simple typealias, with the following specific syntax:

using @pkg {
  value,
  CONST,
  type Type,
  trait Trait,
}

This syntax creates aliases with the same name, allowing you to directly use value, CONST, Type, Trait in the current package to refer to the entity from @pkg.

You can add pub before using to make these aliases visible externally.

You can also use the as keyword for renaming in using statements, for example:

using @pkg {
  value as another_value
}

This creates an alias named another_value referring to @pkg.value. However, we do not encourage using as for renaming as it harms code readability, so the compiler will report a warning when as is used. We will remove this syntax in the future, and it's currently retained to allow migration from fnalias to using.

In addition to using, we also add support for the #alias attribute on types and traits. Therefore, all top-level definitions can now use #alias to create aliases.

The simple typealias mentioned earlier refers to cases where only a type alias is created, such as typealias @pkg.Type as MyType. For more complex type aliases that cannot be created with using, we introduce the type AliasType = ... syntax, for example:

type Handler = (Request, Context) -> Response
type Fun[A, R] = (A) -> R

Overall, in the future we will completely remove traitalias, fnalias, and typealias, replacing them with using, type Alias = ..., and #alias. Specifically:

  • using is used to create aliases of another package in the current package, for example, to import definitions from other packages
  • type Alias = ... is used to create aliases for complex types
  • #alias is used to create aliases for entities in the current package, for example, for migration during naming changes

Using moon fmt can automatically complete most migrations. We will subsequently report warnings for the old typealias/traitalias/fnalias syntax and eventually remove them.

2. Duplicate test name checking. The compiler now reports warnings for duplicate test names.

3. Experimental lexmatch updates

The experimental lexmatch update supports lexmatch? expressions similar to is expressions, and case-insensitive modifier syntax (?i:...). For details, please check the proposal

if url lexmatch? (("(?i:ftp|http(s)?)" as protocol) "://", _) with longest {
  println(protocol)
}

4. Added linting checks for anti-patterns

The compiler now includes linting checks for some anti-patterns, reporting warnings during compilation. Currently includes the following checks:

anti-pattern:

match (try? expr) {
  Ok(value) => <branch_ok>
  Err(err) => <branch_err>
}

Suggested modification:

try expr catch {
  err => <branch_err>
} noraise {
  value => <branch_ok>
}

anti-pattern:

"<string literal>" * n

Suggested modification:

"<string literal>".repeat(n)

5. Added ReadOnlyArray built-in type

The compiler now includes a new ReadOnlyArray built-in type. ReadOnlyArray represents fixed-length immutable arrays, mainly used as lookup tables. When using ReadOnlyArray, if all elements are literals, it is guaranteed to be statically initialized in the C/LLVM/Wasmlinear backends. Its usage is basically the same as FixedArray, for example:

let int_pow10 : ReadOnlyArray[UInt64] = [
  1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
  1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
  100000000000000UL, 1000000000000000UL,
]

fn main {
  println(int_pow10[0])
}

6. Adjustments to bitstring pattern syntax

Previously, bitstring patterns supported using u1(_) pattern to match a single bit. At the same time, MoonBit allows omitting payloads when pattern matching with constructors, for example:

fn f(x: Int?) -> Unit {
  match x {
    Some => ()
    None => ()
  }
}

This could cause confusion when using u1 - whether it's a bitstring pattern or a single identifier. To avoid confusion with regular identifiers, in the new version, we require bitstring patterns to be written in suffix form, such as u1be(_). (Bit reading order is always high-bit first, so endianness has no meaning when reading data with length not exceeding 8 bits; this is just to more explicitly indicate that the current pattern uses bitstring pattern.)

Additionally, because little-endian semantics are more complex, little-endian can only be used when the number of bits read is a multiple of 8, such as u32le(_).

Overall, the current bitstring pattern syntax is u<width>[le|be] where:

  • width range is (0..64]
  • le and be suffixes must be written explicitly; all width values support be, only width values that are multiples of 8 support le

7. #deprecated adds a new parameter skip_current_package

Previously, when using a deprecated definition from the current package marked with #deprecated, the compiler would not report warnings. This was to prevent uneliminable warnings in tests/recursive definitions, but sometimes it's also necessary to detect usage of deprecated definitions within the current package. Therefore, #deprecated adds a new parameter skip_current_package with a default value of true, which can be overridden with #deprecated(skip_current_package=false). When skip_current_package=false, using this definition within the current package will also report warnings.

8. moon.pkg DSL plan

We plan to design a new moon.pkg syntax to replace the original moon.pkg.json. The new DSL will improve the experience of writing import information under three premises:

  • Compatible with existing options
  • Friendly to build system performance
  • Preserve the semantics of import being "effective for the entire package"

Current moon.pkg syntax overview in the proposal (not final):

import {
  "path/to/pkg1",
  "path/to/pkg2",
  "path/to/pkg3" as @alias1,
  "path/to/pkg4" as @alias2,
}

// Blackbox test imports
import "test" {
  "path/to/pkg5",
  "path/to/pkg6" as @alias3,
}

// Whitebox test imports
import "wbtest" {
  "path/to/pkg7",
  "path/to/pkg8" as @alias4,
}

config {
  "is_main": true, // Comments allowed
  "alert_list": "+a+b-c",
  "bin_name": "name",
  "bin_target": "target",
  "implement": "virtual-package-name", // Trailing commas allowed
}

Proposal discussion and details: https://github.com/moonbitlang/moonbit-evolution/issues/18

Build System Updates

1. Deprecate moon info --no-alias.

In the last monthly, we added the moon info --no-alias functionality to generate pkg.generated.mbti files without default aliases:

moon info

package "username/hello2"

import(
"moonbitlang/core/buffer"
)

// Values
fn new() -> @buffer.Buffer

moon info --no-alias

package "username/hello2"

// Values
fn new() -> @moonbitlang/core/buffer.Buffer

In practice, we found this functionality had limited utility while increasing maintenance costs, so we decided to deprecate it.

2. Updated template project generated by moon new.

We added .github/workflows/copilot-setup-steps.yml to the template project generated by moon new to help users quickly use GitHub Copilot in MoonBit projects.

IDE Updates

1. mbti files support find references.

Users can find references of a definition in mbti files within the project:

alt text

As shown, we currently support three different ways to find references:

  • Go to References: Search in all reverse dependencies of the package
  • Go to References (current package only): Search only within the package, including all tests
  • Go to References (current package only, excluding tests): Search only within the package, skipping all tests

2. mbti files support Hide and Unhide code actions, facilitating API refactoring:

alt text

For definitions in mbti, LSP provides a Hide ... code action to make it private. After executing the above code action, Manager will become a private definition:

alt text

If no errors are reported after executing the code action, it means this is a redundant API that can be remove; if there are errors, users can use the Unhide ... code action to undo the previous operation:

alt text

3. Automatically format updated code blocks after test updates.

When using update codelens in VSCode to update tests, the updated tests are automatically formatted.

4. Added more attribute snippet completions.

Standard Library Updates

  1. Added external iterator type Iterator. We will migrate iterators for common container types in core from internal to external. This is to enable async usage in for .. in loops and support APIs like zip that internal iterators cannot support. The for .. in loop now calls .iterator()/.iterator2() methods for iteration through external iterators. When the iterated type doesn't support external iterators, it falls back to the original internal iterator type Iter. In the future, we will migrate from internal iterators to external iterators, and eventually keep only external iterators.

20251014 MoonBit Monthly Update Vol.04

· 4 min read

Version v0.6.29

Language Updates

  • Asynchronous Programming Enhancements

    Added support for async test and async fn main syntax, enabling asynchronous tests and asynchronous main functions. Both async fn main and async test are based on the moonbitlang/async library and are currently supported on the native backend for Linux and macOS. For more details about async programming in MoonBit, see the documentation and GitHub repository of moonbitlang/async. Async tests declared with async test will run in parallel.

    ///|
    async test "http request" {
      let (response, result) = @http.get("https://www.example.org")
      inspect(response.code, content="200")
      assert_true(result.text().has_prefix("<!doctype html>"))
    }
  • Experimental Feature: lexmatch Expression

    Added the lexmatch expression (experimental feature), which enables pattern matching on StringView or BytesView using regular expressions.

    The following example matches two to four consecutive 'a' characters followed by a 'b', capturing the consecutive 'a's as variable a. For more detailed usage, refer to lexer.mbt in moonbitlang/parser and the lexmatch proposal in moonbit-evolution.

    lexmatch x with longest {
      (("a{2,4}" as a) "b", _) => Some(a.length())
      _ => None
    }
  • Unified Import Syntax with using

    Introduced the using syntax, which unifies fnalias, traitalias, and simple typealias. When importing a type or trait, the corresponding keyword must now be prefixed before the name.

    using @pkg {
      value,
      CONST,
      type Type,
      trait Trait,
    }

    In addition, pub using can be used to achieve re-export, allowing definitions from other packages to be re-exported in the current package. In the future, the fnalias, traitalias, and simple typealias syntaxes will be deprecated — the functionality of creating aliases for external definitions will be replaced by using, while creating aliases for definitions within the current package will be handled by the #alias attribute.

  • Methods in a trait now support optional parameters.

    pub(open) trait Reader {
      async read(Self, buf : FixedArray[Byte], offset? : Int, len? : Int) -> Unit
    }

    The default value of an optional parameter is determined individually by each impl. Different implementations can define their own default values or choose not to provide one at all — in that case, the optional parameter’s type within the impl will be T?, where None indicates that the user did not supply the argument.

  • Operator Overloading via #alias

    Added support for using #alias to overload operators such as op_get, providing better readability compared to the op_xxx naming convention.

    // Previously `op_get`
    #alias("_[_]")
    fn[X] Array::get(self : Array[X], index : Int) -> X { ... }
    
    // Previously `op_set`
    #alias("_[_]=_")
    fn[X] Array::set(self : Array[X], index : Int, elem : X) -> Unit { ... }
    
    // Previously `op_as_view`
    #alias("_[_:_]")
    fn[X] Array::view(
      self : Array[X],
      start? : Int = 0,
      end? : Int = self.length(),
    ) -> ArrayView[X] { ... }

    Currently, the following operators are supported. The actual implementation names (e.g., get, set, view) can be freely chosen — simply attach the corresponding #alias to enable operator overloading. It is recommended to use #alias instead of op_xxx for method-based operator overloading. (Note: Operators like + are overloaded through traits and are not affected by this change.)

  • Removal of Long-Deprecated Syntax and Behaviors

    Several language features that had been deprecated for a long time have now been officially removed:

    Previously, methods defined in the form fn meth(self : T, ..) were both methods and regular functions, allowing them to be called directly as standalone functions. This behavior had long been deprecated (with compiler warnings) and is now removed. The declaration fn meth(self : T, ..) is now equivalent to fn T::meth(self : T, ..). In the future, the self-form method definition itself may also be deprecated.

    The direct_use field in moon.pkg.json has been officially removed and replaced by using.

Toolchain Updates

  • Wasm Toolchain Released — The Wasm version of the MoonBit toolchain is now available for x86 Darwin and ARM Linux users. 🔗 Read more

  • Experimental Build System (RR) — A new experimental version of the build system, codenamed RR, has been introduced. It offers higher performance and better maintainability, and will eventually replace the current internal implementation of moon. You can enable it using the environment variable NEW_MOON=1 or the command-line flag -Z rupes_recta. If you encounter any issues, please report them on GitHub Issues.

  • moon fmt Enhancements — Now supports formatting of .mbt.md files.

  • moon info --no-alias Option — Added a new option to hide type aliases when generating the pkg.generated.mbti file.

Standard Library Updates

  • Improved Hash Security — To mitigate potential HashDoS attacks, hash computation is now randomized per process. This change has already been implemented in the JS backend.

  • Immutable ArrayViewArrayView has been changed to an immutable data structure, providing a unified slicing interface for Array, FixedArray, and ImmutArray.

20250908 MoonBit Monthly Update Vol.03

· 4 min read

Language Updates

  1. Support for Bitstring Patterns
  • Added support for Bitstring patterns, enabling matching of specific-width bits when pattern matching on Bytes or @bytes.View, for example:

    pub fn parse_ipv4(ipv4 : @bytes.View) -> Ipv4  {
      match ipv4 {
        [ // version (4) + ihl (4)
          u4(4), u4(ihl),
          // DSCP (6) + ECN (2)
          u6(dscp), u2(ecn),
          // Total length
          u16(total_len),
          // Identification
          u16(ident),
          // Flags (1 reserved, DF, MF) + Fragment offset (13)
          u1(0), u1(df), u1(mf), u13(frag_off),
          // TTL + Protocol
          u8(ttl), u8(proto),
          // Checksum (store; we'll validate later)
          u16(hdr_checksum),
          // Source + Destination
          u8(src0), u8(src1), u8(src2), u8(src3),
          u8(dst0), u8(dst1), u8(dst2), u8(dst3),
          // Options (if any) and the rest of the packet
          .. ] => ...
        ...
      }
    }

    You can use u<width>be or u<width>le to match bits of the specified width in big-endian or little-endian order. If neither be nor le is specified, big-endian is used by default. The valid width range is [1, 64].

  1. Direct use of constructors in pattern matching
  • Allowed direct use of constructors in pattern matching to match only the tag of an enum, for example:
    fn is_some(x: Int?) -> Unit {
      guard x is Some
    }
    Previously, using a constructor with a payload as if it had none would result in a compiler error. This has now been changed to a warning, allowing users to disable such warnings via the warn-list parameter.
  1. Added #callsite(migration) attribute for code migration of optional arguments.

    For example, if the author wants to change the default value of parameter y in the next version, they can use migration(fill=true, ...) to prompt downstream users to explicitly provide a value. Similarly, if the optional parameter z is planned to be removed in the next version, migration(fill=false, ...) can be used to notify users that they no longer need to explicitly provide z.

    #callsite(migration(y, fill=true, msg="must fill y for migration"), migration(z, fill=false, msg="cannot fill z for migration"))
    fn f(x~ : Int, y~ : Int = 42, z? : Int) -> Unit {
      ...
    }
  2. Added #skip attribute to skip tests, for example:

    #skip("Reason for skipping")
    test "will not run" {
      ...
    }

    However, the compiler will still perform type checking on the code inside the test block.

  3. Added #as_free_fn attribute as a replacement for the fnaIias Type::f functionality, for example:

    #as_free_fn // allow MyType::f to be called as f
    #as_free_fn(f0) // allow MyType::f to be called as f0
    #as_free_fn(f1, deprecated) // allow MyType::f to be called as f1, but with deprecated message
    fn MyType::f(self : MyType) -> Unit {
      ...
    }

    This allows MyType::f to be called directly as a regular function f or f0.

  4. Added visibility control to #alias and #as_free_fn, for example:

    #as_free_fn(visibility="pub")
    fn MyType::f(self : MyType) -> Unit {
      ...
    }

    In this case, the MyType::f method is private, but its free function f is public. The same applies to #alias in the following example.

    #alias(pub_alias, visibility="pub")
    fn priv_func() -> Unit {
      println("priv func")
    }
  5. Async functions now implicitly raise.

  • Functions marked with async no longer need an explicit raise, making the code more concise. If you want type checking to ensure that an async function does not throw errors, you can mark it with noraise.
  1. FFI parameter ownership annotations required.
  • In the C/LLVM/Wasm backends, parameters declared as pointers in FFI must now be annotated with either #borrow or #owned; otherwise, the compiler will emit a warning. #borrow means the called FFI function will only read or write the parameter locally and will not store or return it, so no special handling is required. #owned means ownership of the parameter is transferred to the called function.

    For details, see the documentation on external function calls. The motivation for this change is that in the future we plan to switch the default calling convention for FFI parameters from #owned to #borrow, and explicit annotations help users migrate progressively.

  1. Changed calling convention of FuncRef[_].
  • Previously, FuncRef[_] treated parameter ownership as #owned. It has now been changed to #borrow. This change is breaking: users who use FuncRef[_] in FFI and have pointer-type parameters must update their code accordingly.

Toolchain Updates

  • IDE now supports hover and go-to-definition features for .mbti files.

20250811 MoonBit Monthly Update Vol.02

· 5 min read

Language updates

  1. New conditional compilation attribute cfg.
  • You can now compile specific sections of code based on conditions such as the target backend.
    #cfg(any(target="js", target="wasm-gc"))
    let current_target = "js | wasm-gc"
  1. New #alias attribute.
  • You can now create aliases for methods or functions and attach annotation information. More scenarios will be supported in the future.。
    #alias(combine, deprecated="use add instead")
    fn Int::add(x : Int, y : Int) -> Int {
      x + y
    }
    
    test {
      let _ = Int::add(1, 2)
      let _ = Int::combine(1, 2)
    }
  1. New defer statement.
  • Provides a scope-based resource cleanup feature. When any form of defer expr; body appears in the body of a block, the expr will always be executed when the body ends.
    fn main {
      defer println("End of main")
      {
        defer println("End of block1")
        println("block1")
      }
      for i in 0..<3 {
        defer println("End of loop \{i}")
        if i == 2 {
          break // `break` and similar statements can also trigger `defer`
        }
        println("Looping \{i}")
      }
      return
    }
    block1
    End of block1
    Looping 0
    End of loop 0
    Looping 1
    End of loop 1
    End of loop 2
    End of main
    Currently, the expr in a defer expr cannot contain expressions or calls to async functions, and expr cannot use control flow constructs such as return / break / continue.
  1. Native Backend Bytes Terminator Update
  • In the Native backend, the Bytes representation used to always have an extra trailing '\0' character. Now, Bytes can be directly used to pass C strings in FFI calls without this extra trailing '\0' character being counted in the Bytes length, so the current code behavior will remain unchanged.
  1. Optional Parameter Syntax Enhancement and Unification
  • Adjusted the syntax for optional parameters: default arguments can now depend on preceding parameters (this behavior was previously removed due to incompatibility with virtual packages, but we have now found a way to support such complex defaults while remaining compatible).We have also unified optional parameters with default values (label: T = ...) and those without (label?: T). From the caller’s perspective, there is no longer any difference between them, and both now support the following call styles:
    • Omit the argument to use the default value.
    • Pass explicitly using label=value.
    • Use label?=opt, meaning: if opt is Some(value), it is equivalent to label=value; if opt is None, it is equivalent to omitting the argument.
  1. Use #callsite(autofill(...)) as a shorthand for default arguments.
  • When calling functions with default arguments, the #callsite(autofill(...)) attribute can be used as a shorthand:

      // Original code
      pub fn[T] fail(msg : String, loc~ : SourceLoc = _) -> T raise Failure { ... }
      // new code
      #callsite(autofill(loc))
      pub fn[T] fail(msg : String, loc~ : SourceLoc) -> T raise Failure { ... }
  1. Removed newtype
  • added support for tuple struct.

      // Old syntax, accessing the wrapped Int
      type A Int
      fn get(a : A) -> Int {
        a.inner()
      }
    
      // New syntax, accessing the wrapped Int
      struct A(Int)
      fn get(a : A) -> Int {
        a.0
      }
    
      struct Multiple(Int, String, Char)
      fn use_multiple(x: Multiple) -> Unit {
        println(x.0)
        println(x.1)
        println(x.2)
      }
      fn make_multiple(a: Int, b: String, c: Char) -> Multiple {
        Multiple(a, b, c)
      }
  • For a tuple struct with one field, it is equivalent to the original newtype. If the underlying type is not a tuple, the formatter will auto-migrate old access syntax. In this case, an .inner() method is provided for migration and will be deprecated later.

  • For a tuple struct with multiple fields, differences from the original tuple newtype are:

    • Cannot be constructed directly with tuple syntax.
    • No .inner() method to retrieve the tuple.

    For tuple structs that support conversion to a tuple, you can use:

    struct T((Int, Int))
    
    fn make_t(x: Int, y: Int) -> T {
      (x, y)
    }
    
    fn use_t(t: T) -> (Int, Int) {
      t.0
    }
  • In this case, to access specific elements, you need to use t.0.0 or t.0.1.

  1. Since the primary purpose is for data storage and functions such as @json.inspect, derive(FromJson, ToJson) will no longer provide advanced format adjustment parameters.
  • The currently retained format parameters are: rename for each field, batch renaming, and the style option for enum format selection; all other parameters will be removed.
    • The optional values for style are legacy and flat. The latter simplifies representation and is suitable for scenarios such as @json.inspect. All enums must currently choose one of these styles.
    • If you need to customize the JSON format, please implement the FromJson and ToJson traits yourself.
    ///| Flat
    test {
      @json.inspect(Cons(1, Cons(2, Nil)), content=["Cons", 1, ["Cons", 2, "Nil"]])
    }
    
    ///| Legacy
    test {
      @json.inspect(Cons(1, Cons(2, Nil)), content={
        "$tag": "Cons",
        "0": 1,
        "1": { "$tag": "Cons", "0": 2, "1": { "$tag": "Nil" } },
      })
    }

Toolchain update

  1. Added moon coverage analyze for clearer coverage reports.
Total: 1 uncovered line(s) in 2 file(s)

1 uncovered line(s) in src/top.mbt:

   | fn incr2(x : Int, step? : Int = 1) -> Int {
12 |   x + step
   |   ^^^^^^^^         <-- UNCOVERED
   | }


Total: 1 uncovered line(s) in 2 file(s)
  1. moon test --target js now shows original source locations on panic via sourcemap.
test username/hello/lib/hello_test.mbt::hello failed: Error
    at $panic ($ROOT/target/js/debug/test/lib/lib.blackbox_test.js:3:9)
    at username$hello$lib_blackbox_test$$__test_68656c6c6f5f746573742e6d6274_0 ($ROOT/src/lib/hello_test.mbt:3:5)
    at username$hello$lib_blackbox_test$$moonbit_test_driver_internal_execute ($ROOT/src/lib/__generated_driver_for_blackbox_test.mbt:41:9)

20250714 MoonBit Monthly Update Vol.01

· 2 min read

After the beta release on June 18, 2025, Moonbit's syntax will be more stable, and our focus will gradually shift towards performance improvements and ecosystem development. Starting with this announcement, Moonbit updates will be released monthly, with the primary content still centered on language, standard library, and toolchain enhancements.

Language Updates

  • Support for !expr syntax. Boolean expressions can now be directly negated using the ! symbol, without needing the not function.
fn true_or_false(cond: Bool) -> Unit {
  if !cond {
    println("false branch")
  } else {
    println("true branch")
  }
}

fn main {
  true_or_false(true)  // true branch
  true_or_false(false) // false branch
}
  • The else keyword in try .. catch .. else .. syntax has been replaced with noraise. This change was made because the else in try .. catch .. else .. is followed by pattern matching rather than a code block, which is inconsistent with else elsewhere. The old syntax will be deprecated, and the compiler will issue a warning.
  • Functions can now be marked noraise in their return type. This provides clearer documentation in type signatures and can prevent the compiler from automatically inserting a raise mark in certain situations. For example:
fn h(f: () -> Int raise) -> Int { ... }

fn init {
  let _ = h(fn () { 42 }) // ok
  let _ = h(fn () noraise { 42 }) // not ok
}
  • Ellipsis (...) is now allowed to omit code in pattern matching. For example:
fn f(x: Int) -> Unit {
  match x {
    ...
  }
}

Toolchain Updates

  • More powerful code coverage testing. You can now use the moon coverage analyze command to directly identify unused lines of code. For example:
fn coverage_test(i : Int) -> String {
  match i {
    0 => "zero"
    1 => "one"
    2 => "two"
    3 => "three"
    4 => "four"
    _ => "other"
  }
}

test "coverage test" {
  assert_eq(coverage_test(0), "zero")
  assert_eq(coverage_test(1), "one")
  assert_eq(coverage_test(2), "two")
  // assert_eq(coverage_test(3), "three")
  assert_eq(coverage_test(4), "four")
  assert_eq(coverage_test(5), "other")
}
  • Running moon coverage analyze on the code above will first execute the tests and then print the lines not covered during test execution, as shown below:
 moon coverage analyze
Total tests: 1, passed: 1, failed: 0.

warning: this line has no test coverage
 --> main/main.mbt:6
4 |     1 => "one"
5 |     2 => "two"
6 |     3 => "three"
  |     ^^^^^^^^^^^^
7 |     4 => "four"
8 |     _ => "other"
  • This tool will be a great help in guiding your testing efforts.

Standard Library Updates

  • Reminder: JSON data definitions will change in the next version. Please do not directly use constructors; instead, use functions like Json::number to construct JSON values.

2025-06-16

· 6 min read

Language Updates

  1. Error Function Syntax Updated to raise
  • The ! syntax for representing errors has been replaced with the keyword raise. The specific mappings are as follows:

    • (..) -> T ! SomeErr => (..) -> T raise SomeErr

    • (..) -> T ! => (..) -> T raise

    • (..) -> T ? Error => (..) -> T raise? (This is a recently added error polymorphism syntax; it can be ignored if unfamiliar.)

    • fn f!(..) { .. } => fn f(..) raise { .. }

    • fn!( ..) { .. } => fn (..) raise { .. }

    The above changes can be auto-migrated using code formatting.

  1. Error Type Declaration Syntax Changed
  • The syntax for defining error types, type! T .., has been changed to suberror T ... This change can also be automatically migrated using code formatting.
  1. Deprecation of f!(..) and f?(..)
  • The f!(..) and f?(..) syntaxes have been deprecated. Continued use of them will trigger compiler warnings. Code formatting can automatically remove ! for migration, but f?(..) requires manual migration to try?. This is because for cases like f?(g!(..)), a simple change to try? f(g(..)) would alter the semantics, causing errors in g to also be caught. Special attention is needed during manual migration of f?(..).
  1. Function Type Parameter Syntax Aligned with impl
  • Several weeks ago, the position of type parameters in function definitions was moved from fn f[..](..) to fn[..] f(..) to align with impl. Now, the old syntax is deprecated and will trigger compiler warnings. This change can be automatically migrated using code formatting.
  1. Simplified Syntax for typealias and traitalias
  • Use typealias B as A and traitalias B as A instead of typealias A = B and traitalias A = B. Example:

    Old: typealias Matrix[X] = Array[Array[X]]

    New: typealias Array[Array[X]] as Matrix[X]

    This change can be auto-migrated.

  1. Multi-parameter loop syntax deprecated
  • The multi-parameter loop syntax has been deprecated and should be replaced with a loop that takes a tuple as its parameter. This change aligns loop with match more consistently. The MoonBit compiler can optimize away the overhead of tuples in loop in release mode, so there is no need to worry about performance impacts.
  1. Trait Method Implementation Now Explicit
  • For traits where "every method has a default implementation," previously all types would automatically implement them. Now, even if all methods of a trait have default implementations, explicit implementation is still required. If no custom implementation is needed, impl Trait for Type can be used to indicate "implement Trait for Type using all default methods." impl Trait for Type can also serve as documentation or a TODO. MoonBit will automatically check whether Type implements Trait and report an error if it does not.
  1. Cannot Use Dot Syntax Call Implementation Method for External Types
  • Previously, impl for external types could be called within the current package using .. However, this feature was not refactoring-safe: upstream additions of methods could alter the behavior of downstream code. Therefore, this behavior has been deprecated. As an alternative, MoonBit now supports locally defining new methods for external types, with syntax identical to regular method definitions. Methods defined for external types have the following characteristics:

    • They cannot be pub. This ensures no conflicts arise during cross-package collaboration.

    • If the upstream package (where the type is defined) already has a method with the same name, the compiler will issue a warning.

    • Local methods have the highest priority during method resolution.

    After this change, the resolution rules for x.f(..) are as follows (in order of priority):

    1. Local methods
    2. Methods from the package where x's type is defined
    3. impl from the package where x's type is defined
  1. Auto Insertion of to_json for JSON Literals
  • Within JSON literals, the compiler now automatically inserts ToJson::to_json calls for non-literal expressions, making JSON literals more convenient to write:
let x = 42
// Previously
let _ : Json = { "x": x.to_json() }
// Now
let _ : Json = { "x": x }
  1. Virtual Package Support for Abstract Types
  • The virtual package feature now supports abstract types. Abstract types can be declared in .mbti interfaces, and different implementations can use different concrete types to fulfill the interface's abstract types.
  1. Simplified Syntax for Error Handling
  • For handling errors in simple expressions, try can be omitted, and f(..) catch { .. } can be written directly.
  1. Reserved Words Warning Mechanism
  • A new set of reserved words has been added. These are not currently keywords but may become keywords in the future. Using these names in code will trigger compiler warnings.

Upcoming Changes to Be Released Before the MoonBit Beta on June 18

  1. New Arrow Function Syntax (..) => expr

    This syntax simplifies defining single-parameter inline functions. Example:

test {
  let arr = [ 1, 2, 3 ]
  arr
    .map(x => x + 1) // Parentheses can be omitted for single parameters
    .iter2()
    .each((i, x) => println("\{i}: \{x}"))
}
  1. Matrix Function Syntax Simplified

    Matrix functions have been deprecated to simplify the syntax. Matrix functions of the form fn { .. => expr } can be replaced with arrow functions, while other matrix functions should be replaced with explicit fn and match.

  2. Deprecation of xx._ Syntax in new type Definitions

    Previously, the xx._ syntax could be used to convert a newtype into its underlying representation. However, this syntax was visually ambiguous with partial application syntax (_.f(..)). Therefore, the xx._ syntax has been deprecated. Instead, the compiler will automatically generate an .inner() method for each newtype to replace ._. This change can be automatically migrated using code formatting.

  3. Warnings on Ambiguous Precedence

    For ambiguous or less widely known operator precedence combinations, such as << and +, MoonBit will now issue warnings. Adding parentheses manually or via code formatting will resolve the warnings.

  4. Introduction of letrec and and for Local Mutual Recursion

    The letrec and and keywords have been introduced for declaring locally mutually recursive functions, e.g.:

fn main {
  letrec even = fn (x: Int) { ... } // Anonymous function
  and odd = x => ... // Arrow function
}
  • The right-hand side of the equals sign must be a function-like value, such as an anonymous function or arrow function. The previous implicit mutual recursion syntax using fn will be deprecated, though self-recursive functions can still be declared with fn.*
  1. fnalias Cannot Be Used to Define Non-Function values

    The fnalias keyword is now restricted to function definitions only. Use let to define other types of values.

Standard Library Updates

  • Leveraging the new error polymorphism feature, many higher-order functions in the standard library, such as Array::each, can now accept callback functions that may raise errors.

Toolchain Updates

  • Tests can now be written in the main package. moon test will run tests in the main package, while moon run will execute the main function.
  • The IDE's codelens now supports running tests in documentation.
  • moon test and moon check now include tests in documentation by default.

MoonBit Beta Launch on June 18 — Developer Updates Transition to Monthly Reports

After extensive refinement and continuous improvement based on community feedback, the MoonBit Beta version will be officially released on June 18, marking the transition to a more stable stage of the language.

Starting from this milestone, the MoonBit biweekly report will shift to a monthly cadence. Please stay tuned to this column for future updates.

We also welcome community feedback and discussion — feel free to share your thoughts and suggestions with us!

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.