Skip to main content

weekly 2024-07-15

Β· 4 min read

Language Update​

  • New syntax: Cascade Operator

MoonBit introduces the ".." operator, allowing for chaining of mutable API calls while keeping the mutable API signatures clean (still returning Unit).

As illustrated, to avoid repetitive typing of array and to distinguish between immutable and mutable operations, we apply the cascade operator. For all methods within a type that return Unit, you can use .. to chain these method calls without needing to change the return type of these methods. array..push(5)..sort() is equivalent to sequentially calling the mutable operations array.push(5) and array.sort(), returning array.

cascade.png

  • The @package.Type::[...] syntax has been removed. It is recommended to use @package.of([...]) as a replacement.

  • Sourcemap Improvement: Debug mode sourcemaps generated with --target js now support the names table. This means that when debugging with devtools or VSCode, local variables will be displayed with their original names and can be accessed with their original names in the Debug console. This greatly improves the debugging experience.

Before: names_before.png

After: names_after.png

  • Spread Syntax for Arrays: Arrays now support spread syntax, which allows any object expression that supports the iter() method to be expanded into a set of Iter[_] objects at the syntax level during array construction.

Array Expression:

let u1 = [1, 2, 3]
let u2 = [5, 6, 7]

fn main {
let v = [..u1, 4, ..u2, 8]
println(v)
}

// [1, 2, 3, 4, 5, 6, 7, 8]

Map Expression (returns key-value pairs):

  let map : @sorted_map.T[Int,String] = @sorted_map.of([(3, "c"), (2, "b"), (1, "a")])

fn main {
let v = [..map, (4, "d")]
println(v)
}

// [(1, a), (2, b), (3, c), (4, d)]
  • C-like Bitwise Operators Support: MoonBit now supports C-like bitwise operators (^, &, |, <<, >>) with the same precedence as in C. To avoid ambiguity, when expressions contain several operators with unclear precedence, the formatter will insert additional parentheses to improve readability.

  • Integer Literal Overloading: For known types, literals for types other than Int can omit the special markers like U, L, etc:

fn main {
let uint : UInt = 42
let int64 : Int64 = 42
let double : Double = 42
}

Core Update​

  • The Hash Trait will be updated to the following form.
trait Hash {
hash_iter(Self, Hasher) -> Unit
hash(Self) -> Int
}

impl Hash with hash(self) {
let hasher = Hasher::new()
hasher.hash_iter(self)
hasher.finalize()
}

Build System Update​

Testing Mechanism Adjustment​

Moon now automatically wraps source code files ending in _bbtest.mbt as black-box tests. Moon will automatically include the package as a dependency when compiling *_bbtest.mbt. We'll continue to adjust MoonBit's testing mechanism for a better developing and testing experience.

Background​

Currently, a MoonBit project can have three types of tests: white-box tests, black-box tests, and inline tests.

  • White-box tests: Written in *_test.mbt, the build system packages and compiles the current package's *.mbt and *_test.mbt together. Therefore, *_test.mbt can access private members of the current package. These tests can use dependencies specified in the import and test-import fields in moon.pkg.json. test-import is only used in white-box tests and will not be packaged into the final build product.

  • Black-box tests: Written in *_bbtest.mbt, the build system automatically includes the package as a dependency when compiling *_bbtest.mbt. *_bbtest.mbt can only access public members of its package, simulating the perspective of an external user using the package. These tests can use dependencies specified in the *_bbtest.mbt field in moon.pkg.json (as well as the package itself, which does not need to be explicitly listed in the bbtest-import field). bbtest-import is only used in black-box tests and will not be packaged into the final build product.

  • Inline tests: Can be written directly in *.mbt(note that *.mbt here excludes the above-mentioned *_test.mbtand *_bbtest.mbt) and can access private members of the current package. These tests only use dependencies specified in the import field in moon.pkg.json.

Future Test Naming Change​

In the future, the current *_test.mbt (white-box tests) will be renamed to *_wbtest.mbt, and *_bbtest.mbt (black-box tests) will be renamed to *_test.mbt to encourage black-box tests.

Toolchain Update​

  • moonfmt improvements:

    • When there are syntax errors in the source code, formatting will ignore the relevant code, allowing code formatting to still be executed.

    • When expressions contain several operators with unclear precedence, the formatter will insert additional parentheses to improve readability.

  • MoonBit IDE added support for black-box tests (*_bbtest.mbt).

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