Skip to main content

weekly 2024-07-08

Β· 3 min read

Language Update​

  • [Breaking change] Modified array slice syntax from arr[start..end] to arr[start:end] similar to Python. This change is made to avoid syntax conflicts with the upcoming cascade method call x..f(). The old syntax will be deprecated soon.

  • [Breaking change on wasm backend] Code in fn init is now compiled into the start section. Previous versions compiled code from both fn init and fn main into a special function exported as "_start", therefore, host environments needed to call "_start" for initialization and execution of the main function. The new version uses the wasm standard's start section to execute fn init during module loading, eliminating the need to call "_start" for fn init. "_start" is only necessary when executing main.

  • [Breaking change] test block no longer returns Result type results. Error handling mechanisms are now used within test blocks to handle test failures. The standard library's inspect function and helpers like @test.eq from the @test package are encouraged for writing tests, for example:

test "unwrap should return value on Some" {
let some : Int? = Some(42)
inspect(some, content="Some(42)")!
@test.eq(some.unwrap(), 42)!
}
  • Supports using @pkg.C to access constructors across packages. For example, if @pkgA contains the following declaration:
// pkgA
pub enum E1 { C1 }

Now, in another package, E1's constructor C1 can be directly used, for instance:

// pkgB
fn main {
debug(@pkgA.C1)
}

In the same package, encountering duplicate public constructors will result in an error, for example:

pub enum E1 {
C1
}

pub enum E2 {
C1
^^ ------ There can be at most one public constructor with name C1.
}

Core Update​

  • Migrated to a new error handling mechanism.

  • Migrated to unsigned integers, removed old compare_u, div_u, mod_u functions for Int and Int64 types; API adjustments include:

    • Int32.trunc_double_u, Int64.trunc_double_u changed to UInt.trunc_double, UInt64.trunc_double.
    • Int64::extend_i32_u changed to UInt64::extend_uint.
    • Double::convert_i32_u, Double::convert_i64_u changed to Double::convert_uint, Double::convert_uint64.

Build System Update​

  • moon version --all displays moonrun's version information:
$ moon version --all
moon 0.1.20240705 (0e8c10e 2024-07-05) ~/.moon/bin/moon
moonc v0.1.20240705+7fdd4a042 ~/.moon/bin/moonc
moonrun 0.1.20240703 (52ecf2a 2024-07-03) ~/.moon/bin/moonrun
  • Modified moon new project creation to default the license field to empty.

  • Diagnostics information rendering is now enabled by default. render

Toolchain Update​

  • VSCode plugin defaulted to installing the corresponding plugin version's toolchain, changed from always installing the latest version.

weekly 2024-07-01

Β· 2 min read

Language Update​

  • Enabled simplifying the type prefix for enum constructor: When there is no ambiguity, you can omit the type prefix for enum constructors. For example, you can now write Some(42) instead of Option::Some(42). If there are two types T1 and T2 that both define a constructor C, you need to specify T1::C or T2::C by the type or type prefix in the context while using, otherwise the compiler will throw an error.

  • New UInt64 built-in type: Added a built-in UInt64 type that supports addition, subtraction, multiplication, division, modulus, and conversion between UInt64 and Int64.

fn main {
let a = 0UL
let b : UInt64 = 1UL
println(a - b) //18446744073709551615
}
  • Support for error handling with !! suffix: The semantics of the !! suffix have been modified to capture potential errors in function calls and return a Result type.
fn f() -> Int!String { .. }
fn main {
let res = f()!! // res: Result[Int, String]
println(res)
}
  • moon test now supports using error types to represent test failures. For example:
fn eq[T : Debug + Eq](a : T, b : T, ~loc : SourceLoc = _) -> Unit!String {
if a != b {
let a = debug_string(a)
let b = debug_string(b)
raise ("FAILED: \(loc) `\(a) == \(b)`")
}
}

test "test_eq" {
eq(1+2, 3)!
}
  • The standard library now retains only println for I/O operations. Other operations will be provided in the io package.

Core Update​

  • Unified the function style for creating container objects, like T::new()/T::make(), and removed T::with_capacity.

  • Renamed iter and iteri to each and eachi, and iter_rev and iter_revi to each_rev and eachi_rev.

  • Renamed as_iter to iter.

Build System Update​

  • The build system will be open source this week.

Toolchain Update​

  • Support for out-of-box debugging for better tooling experience. Users can now run moon run --target js --debug in the JavaScript Debug Terminal for debugging.

  • moon info and coverage tools now accommodate error types and error handling.

weekly 2024-06-24

Β· One min read

Language Update​

  • Support 32-bit unsigned integers

    let num = 100U // Literal for 32-bit unsigned integers requires the 'U' suffix
  • WASM Backend Export Improvements

    When exporting functions with a return type of Unit in the WASM backend, the exported function previously had a type of (result i32). Now, the MoonBit compiler will automatically generate and export a wrapper function with no return value.

  • moonbitlang/core API Consistency Adjustments

    • Unified forall/exist and all/any to all/any
    • Unified contains and member to contains

IDE Updates​

  • Bug Fix

    Fixed a bug where the type prefix would be lost when renaming methods.

  • Enhancement

    Added autocomplete functionality for match clauses of try ... catch ... expressions.

Build System Updates​

  • Diagnostic Information Rendering

    Added an experimental feature for rendering diagnostic information. This can be enabled by setting the environment variable MOON_RENDR=1.

    diagnosis.png

  • Command Changes

    Changed the moon bench command to moon generate-build-matrix. The bench subcommand will be used for future purposes.

weekly 2024-06-17

Β· 3 min read

Language Update​

  • Supported error handling:
  1. Functions can now specify return types with error handling using the syntax Int!String. The following example means the function returns an Int under normal conditions, and throws an error of type String otherwise.
fn div(x: Int, y: Int) -> Int!String { .. }
  1. The raise keyword is used to interrupt the current control flow and throw an error. For example:
fn div(x: Int, y: Int) -> Int!String {
if y == 0 { raise "divide by 0" }
x / y
}
  1. The expression try { expr0 } catch { pattern1 => expr1; pattern2 => expr2; .. } can be used to catch errors thrown in expr0 and handle them with pattern matching. For example, the following function calls the div function and prints the error message if an error is thrown, then returns a default value:
fn div_with_default(x: Int, y: Int, default: Int) -> Int {
try {
div(x, y)!
} catch {
s => { println(s); default }
}
}
  1. Additionally, the suffix operators ! and !! are available for error handling. These operators can only be used on function calls: f(x)! rethrows any error immediately. f(x)!! panics on any error, equivalent to:
try { f(x)! } catch { _ => panic() }

Function calls include method calls, infix operators, and pipeline operators, such as:

fn init {
let _ = x.f()!!
let _ = (x + y)!!
let _ = (x |> f)!!
}
  1. Last, functions that might throw errors but do not use any of the above error handling mechanisms will result in an "unhandled error" error.
  • Support Map literal syntax.
fn init {
// Keys must be literals
let m1 : Map[String, Int] = { "x": 1, "y": 2 }
let m2 : Map[Int, String] = { 1: "x", 2: "y" }
}

IDE Update​

  • Fixed a bug where methods from the builtin package would appear twice during autocompletion.

  • Fixed a bug where the Byte type was missing from autocompletion options.

Build System Update​

  • Added support for internal packages. These packages are placed in a directory named internal and can only be imported by packages rooted in the parent directory of internal.

    For example, if a package is located at username/hello/x/internal/a, its parent directory is username/hello/x. Only username/hello/x and its subpackages (e.g., username/hello/x/a) can import username/hello/x/internal/a. However, username/hello/y cannot import this package.

weekly 2024-06-11

Β· 2 min read

Language Update​

  • Wasm MVP: Added reference counting support based on the Perceus algorithm to the Wasm1 backend.

  • Syntax: throw, raise, try, and catch are reserved as keywords in preparation for the upcoming error handling mechanism.

  • Core: List and sorted_map have been moved to core/immut

    • List has been moved to the core/immut/list package and is no longer supported as a built-in type.
    let a = @immut/list.List::Cons(1, Cons(2, Nil))
    • sorted_map has been moved to the core/immut/sorted_map package.
  • Core: JSON API has been optimized for better performance and to align with the new Core API.

    • New type definitions:
// Types and methods
pub enum JsonValue {
Null
// Previously Boolean(Bool)
True
False
Number(Double)
String(String)
Array(Array[JsonValue])
Object(Map[String, JsonValue]) // Previously @map.Map
}
  • JS: Optimized Int64 performance
    • On the JS backend, Int64 now compiles to two Int values, addressing the previous performance issues with compiling to BigInt. Additionally, the runtime implementation of Int64 in JS has been moved to the core standard library to facilitate open-source community review.

Build System Update​

  • For moon.mod.json and moon.pkg.json, comments are supported when developing, but comments are not allowed when publishing (only standard JSON format is supported).

IDE Update​

  • LSP: Function completion now displays parameter names. LSP.png

weekly 2024-06-03

Β· 2 min read

Language Update​

  • A new syntax T? has been added for type annotations to represent Option[T].
struct Cell[T] {
val: T
next: Cell[T]?
}

fn f(x : Cell[T]?) -> Unit { ... }

This is equivalent to

struct Cell[T] {
val: T
next: Option[Cell[T]]
}

fn f(x : Option[Cell[T]]) -> Unit { ... }

The old Option[T] is still compatible, but it is recommended to use the shorter new syntax. moonfmt will also format Option[T] as T?.

  • The core library API restructuring continues
    • The Iter package has been merged into the builtin package. Now Iter[T] can be used without the @iter. prefix.
pub fn any[T](xs : Iter[T], predicate : (T) -> Bool) -> Bool {
// ^no @iter. is required
match xs.find_first(predicate) {
None => false
Some(_) => true
}
}
  • The Stack package has been moved to moonbitlang/x

  • The List package has been removed, along with the to_list and from_list functions for various data structures. It is recommended to use Iter[T] and Array[T] for data structure conversion and intermediate representation.

  • Performance improvements

    • The compiler will now perform partial closure conversion during the separate compilation phase, improving compilation performance. Closure conversion has also been applied to code generated for the JavaScript backend in specific situations.
    • The types Option[Bool], Option[Char], Option[Byte], and Option[Unit] are now represented using 32-bit integers, where None is represented by -1 and Some(x) by x. The type Option[Int] is represented by a 64-bit integer in the wasm backend, where None is represented by 0x1_0000_0000, and Some(x) by x. In the JavaScript backend, Option[Int] is represented as int | undefined , where undefined represents None.
  • abort behavior change

    • To remove the dependency of Wasm programs on the non-standard spectest.print_char, the error output functionality is being restructured.
    • abort will no longer use spectest.print_char to print error messages. Its behavior will be the same as panic until the functionality is further improved.

    Plugin Update​

  • [Language Server] Fixed a memory leak issue.

weekly 2024-05-27

Β· 2 min read

MoonBit is a Rust-like programming language (with GC support) and toolchain optimized for WebAssembly.

Language Update​

  • Breaking Change: Restructure APIs of MoonBit Core library.
    • Placing all immutable data structures under the immut path, for example, from @immutable_hashmap.Map to @immut/hashmap.Map.
// Before
let a : @immutable_hashmap.Map[Int, Int] = @immutable_hashmap.make()
// After
let a : @immut/hashmap.Map[Int, Int] = @immut/hashmap.make()
  • Performance optimization for the Option[T] type in the core library:
    • When the type T is a reference type, for values of type Option[T], Some(v) will be directly compiled into v, and None will be compiled into ref.null in the wasm-gc backend, or undefined in the JavaScript backend, thus avoiding memory allocation.
  • Introduced fn panicT -> T function in the core library, which can be used within test blocks where the test name must start with "panic":
test "panic test ok" {
panic() // Test passes
}

test "panic test failed" {
() // Test fails
}

IDE Update​

  • VS Code extension: Added test and for code snippets. test snippet: test.gif for snippet: for.gif

Build System Update​

  • Initialization: moon new now automatically initializes version control for new projects, currently supporting git
  • Testing: You can now specify multiple packages to test
moon test -p a b c
moon test -p a -p b -p c

Toolchain Update​

  • Installation: You can now specify a version number for installation
# Mac and Linux Users
# Download the latest version
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
# Download the bleeding edge version
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s bleeding
# Download a specific version
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s 0.1.20240520+b1f30d5e1
# Windows Users
# Download the latest version
irm cli.moonbitlang.cn/install/powershell.ps1 | iex
# Download a specific version
$env:MOONBIT_INSTALL_VERSION = "0.1.20240520+b1f30d5e1"; irm cli.moonbitlang.cn/install/powershell.ps1 | iex

weekly 2024-05-20

Β· 3 min read

MoonBit is a Rust-like programming language (with GC support) and toolchain optimized for WebAssembly.

Language Update​

  • Breaking Change: Rename Array to FixedArray, @vec.Vec to Array
// Before
fn init {
let array : @vec.Vec[Int] = [1, 2, 3]
}
// After
fn main {
let array : Array[Int] = [1, 2, 3]
}
  • Add pattern matching support for Map, HashMap.
    • The type must implement the op_get method, where the key is a native type (Int, Char, String, Bool, etc.), and the value is an Option[T].
    • When matching, the key must be a literal.
    • In { "key": pat }, the pattern pat type is Option[T]. None means "key" does not exist, Some(p) means "key" exists, and p will be used to match the value of this key.
    • The pattern for matching key-value pairs is open: unmatched keys will be ignored even if they exist.
    • Key-value pair patterns will generate optimized code, with each key being queried at most once.
fn main {
let map = @map.Map::[ ("a", 1) ]
match map {
// Match when `map` contains "b",
// and bind the value of "b" in `map` to `y`.
{ "b": Some(y) } => println(y)
// Match when `map` does not contain "b" but contains "a",
// and bind the value of "a" to `k`.
{ "b": None, "a": Some(k) } => println(k)
// The compiler warns that the case { "b": None, "a": None } is not matched.
}
// Output: 1
}
  • Allow omitting the newtype constructor when type information is known.
type A Int

pub fn op_add(self : A, other : A) -> A {
self.0 + other.0 // omit the constructor
}

fn main {
A::A(0) + 1 |> ignore // omit the constructor of 1
let _c : A = 0 + 1 + 2
}

Build System Update​

  • Configuration file options are converted to kebab-case (we'll still support snake_case for a while).
{
"is-main": true,
"test-import": []
}
  • Wasm, Wasm-GC: The backend supports specifying the exported memory name (default is moonbit.memory) and compile options (e.g., -no-block-params for compatibility with the Binaryen toolchain) in moon.pkg.json.
{ 
"link": {
"wasm": {
"export-memory-name": "custom_memory_name",
"flags": ["-no-block-params"]
},
}
  • moon check adds a --deny-warn option, treating warnings as failures and returning a non-zero exit code.
  • Optimized the execution speed of moon fmt and moon info.
  • moon fmt adds a --check option to check if the current code is formatted.

Core Update​

  • Added an experimental library moonbitlang/x for developing and testing packages with unstable APIs. Once packages in moonbitlang/x are stable, we will select important packages to merge into moonbitlang/core based on community feedback.
    • num, time, uuid, and json5 have all been moved to moonbitlang/x.
  • The Bytes API moved from the Int type to the Byte type.
fn Bytes::op_get(self : Bytes, index : Int) -> Byte
fn Bytes::op_set(self : Bytes, index : Int, value : Byte) -> Unit
fn Bytes::length(self : Bytes) -> Int
fn Bytes::make(len : Int, ~init : Byte = b'\x00') -> Bytes

weekly 2024-05-13

Β· 4 min read

MoonBit is a Rust-like programming language (with GC support) and toolchain optimized for WebAssembly.

Language Update​

  • Support mutable fields in the payload of the constructor, here is how to use it:
enum E {
C(mut ~x : Int, mut ~y : Int)
} derive(Debug)


fn swap_xy(x : E) -> Unit {
match x {
// `~y` will bind to the value of the `y` field in the `C` before pattern matching.
// When encountering a pattern like `C(..) as c`, the compiler will know that `c` must be the constructor,
// so `c.x` and `c.y` can be directly accessed within the branch to access the fields of `C`.
C(~y, ..) as c => {
// `c.x` and `c.y` can be used to modify/read the latest values in `C`.
c.y = c.x
c.x = y
}
}
}

fn init {
let e : E = C(x=1, y=2)
debug(e) // C(x=1, y=2)
swap_xy(e)
debug(e) // C(x=2, y=1)
}

In this example, the swap_xy function swaps the values of the x and y fields of the constructor C, and this swap is done in-place without introducing additional memory allocation.

  • Array literals by default construct vectors. Array literal syntax has been overloaded. Now, array literals can be used to construct Vectors and Arrays, with the specific type determined by the context. If the type is ambiguous in the context, Vector is used by default, for example:
fn init {
let v = [1, 2, 3] // v has type @vec.Vec[Int]
let a : Array[_] = [1, 2, 3] // a has type Array[Int]
}

In the future update, Array will be FixedArray, Vec will be Array.

  • Error messages now include error codes, for example:
./vec/sort.mbt:68:16-68:23 [E4021] The value identifier minimum is unbound.
./vec/sort_by.mbt:90:16-90:23 [E4021] The value identifier minimum is unbound.
./vec/vec.mbt:962:37-962:50 [E4020] Package "iter" not found in the loaded packages.
./vec/vec.mbt:963:3-963:13 [E4024] The type/trait @iter.Iter is not found.

IDE Update​

  • Support functionalities like gotodef/gotoref/rename for the labels of labeled fields in constructors, for example: label.PNG

Build System Update​

  • Support configuring the warn list and alert list at the package level.
    • Configuration in moon.pkg.json is as follows, where you can disable corresponding warns and alters during compilation. (Here, 2, alter_1, and alert_2 are respectively the compiler's predefined warn id for Unused variable and user-defined alert id.)
{
"warn_list": "-2",
"alert_list": "-alert_1-alert_2"
}
  • - represents closing the corresponding id's warn and alter. Check the predefined warns with moonc build-package -warn-help.

  • The default backend for moon check|build|run|test has been switched from wasm to wasm-gc

  • The default execution mode for moon test has been changed from release to debug.

  • moon check|build|run|test now supports automatic dependency installation without the need to manually execute moon install

  • moon doc --serve now supports specifying the address and port.

-b, --bind <BIND> [default: 127.0.0.1]
-p, --port <PORT> [default: 3000]
  • Optimize moon size.
PlatformBeforeAfter
macOS arm647.3 MiB3.6 MiB
macOS x86_648.2 MiB4.1 MiB
Ubuntu x86_6414.0 MiB9.6 MiB
Windows x86_649.4 MiB4.9 MiB

Toolchain Update​

  • moonrun now supports printing backtrace. Sample: Create a new project with moon new hello, and replace main/main.mbt with:
  fn bar() -> Unit {
abort("")
}

fn foo() -> Unit {
bar()
}

fn main {
foo()
}

Execute moon run main --debug, and you will see output similar to:

error: RuntimeError: unreachable
at $username/hello/main.bar.fn/1 (wasm://wasm/6fe99e5e:wasm-function[3]:0xe6)
at $username/hello/main.foo.fn/2 (wasm://wasm/6fe99e5e:wasm-function[4]:0xea)
at *init*/3 (wasm://wasm/6fe99e5e:wasm-function[5]:0xef)
at <anonymous>:9:22

weekly 2024-04-30

Β· 4 min read

MoonBit is a Rust-like language (with GC support) and toolchain optimized for WebAssembly experience. This is our recent update:

MoonBit Update

  • MoonBit now supports JavaScript backend, up to 25x faster than native JS and 8x faster than Json5!

Read our latest blog and see how we did it: https://www.moonbitlang.com/blog/js-support

Play with this example and try MoonBit JS now: https://github.com/moonbit-community/benchmark-202404/tree/master/bench_json5

  • MoonBit now allows passing callback functions to FFI. However, the parameters and return types of callback functions are currently limited to simple types such as Int. Example:
// Example: passing a callback to external code
fn set_callback(f : () -> Int) = "ffi" "set_callback"

// Example
fn use_callback() -> Int = "ffi" "use_callback"

test {
let i = { val : 0 }
set_callback(fn () {
i.val += 1
i.val
})
inspect(use_callback(), content="1")?
inspect(use_callback(), content="2")?
inspect(use_callback(), content="3")?
inspect(use_callback(), content="4")?
}
let f

export default {
// Necessary external interface: converting a closure to a JS function. Implementation varies in different runtimes depending on the language.
'moonbit:ffi': {
make_closure: (funcref, context) => funcref.bind(null, context)
},
// Example
ffi: {
set_callback: (func) => {
f = func
}, // Set callback function
use_callback: () => f() // Use callback function
}
}
  • Modified the syntax for explicitly implementing a trait (extension method), allowing explicitly specifying which type is implementing the trait.
// Provide a default implementation for method method of trait Trait
impl Trait with method(...) { ... }

// Implement method method of trait Trait for type Type
impl Trait for Type with method(...) { ... }

// With type parameters
impl[X] Trait for Array[X] with method(...) { ... }

Compared to the previous syntax fn Trait::method(...), the new syntax allows explicit specification of the implementing type, providing richer and clearer signature information. Since the type is specified, the compiler can automatically infer the method's parameter and return types, eliminating the need for manual annotations:

trait MyTrait {
f(Self) -> Option[Int]
}

// No need to annotate `self` and the return type
impl MyTrait for Int with f(self) {
// Compiler can automatically infer that the return type is `Option[Int]`
Some(self)
}
  • Support Bytes literals.

Bytes literals b"..." will convert the ASCII string inside the double quotes to the Bytes type, with support for hexadecimal and octal escapes within the string.

 let b1 : Bytes = b"ASCII"
let b2 : Bytes = b"\xFF\o000"
  • Fixed the issue where { x } is too ambiguous.

Now { x } will be parsed as a struct with only one x field, equivalent to { x: x }. For this potentially confusing syntax, the compiler will issue a warning.

IDE Update

  • Added the option moonbit.autoUpdate in plugins to control whether to enable automatic update prompts.
  • Plugins now support multi-backend development:
    • Users can choose different backends within VSCode.
    • The code diff visibility decreases for non-current backends.

plugins.gif

Build System Update

  • Support for backend-specific files, where these files have extensions named with .wasm|wasm-gc|js.mbt, for example: foo.wasm.mbt, foo.wasm-gc.mbt, foo.js.mbt. For instance, in moon build --target js, only regular .mbt files and .js.mbt files will participate in the build. Accordingly, modifications have been made to moon check|build|test and the linking process in moonbitlang/core to be backend-specific.
  • In the moon.pkg.json file, a new format field is added to ["link"]["js"], used to specify the output JavaScript module format. Possible values are esm|cjs|iife, with the default being esm.
    • In esm mode, export { x as y } statements are used for exporting.
    • In cjs mode, exports.y = x is used for exporting.
    • In iife mode, globalThis.y = x is used for exporting.

Example moon.pkg.json:

{
"link": {
"js": {
"exports": [
"hello"
],
"format": "cjs"
}
}
}

  • moon test -u now supports automatically updating multiple inspect functions within test blocks, and adds the -limit option to restrict the maximum number of iterations for automatic updates.

Toolchain Update

  • moonrun has switched from wasmtime to V8, now supporting the wasm-gc backend.
moon new hello
cd hello
moon run main --target wasm-gc
  • moon info update
    • Support grouping methods to facilitate comparison between implementations of different data structures.
    • Support tagged parameters, default parameters, and automatic parameter filling.
type Map
impl Map {
debug_write[K : Debug, V : Debug](Self[K, V], Buffer) -> Unit
elems[K, V](Self[K, V]) -> List[V]
fold[K, V, T](Self[K, V], (T, V) -> T, ~init : T) -> T
from_array[K : Compare + Eq, V](Array[Tuple[K, V]]) -> Self[K, V]
// ...
}