Skip to main content

· One min read

MoonBit Update

  • Support array.iter intrinsic and annotate functions in the standard library, enabling inline loops in specific cases to improve runtime efficiency.

    /// @intrinsic %array.iter
    pub fn iter[T](self : Array[T], f : (T) -> Unit) -> Unit {
    for i = 0; i < self.length(); i = i + 1 {
    f(self[i])
    }
    }

Toolchain Update

  • Support the experimental code coverage tool:
    • The tool has experimentally supported MoonBit Core CI.
    • We are optimizing the user-facing interface.

image

image

  • By default, the unsafe/throw/raise warnings in Alerts pragmas are turned off, while deprecated alerts are enabled.
  • moonfmt
    • Fixed an issue where comments were misaligned after printing literals with negative signs.
    • Fixed an issue where parentheses disappeared after printing literals with negative signs.
  • Fixed variable highlighting.
  • moonrun now supports printing UTF-16 strings.

Build System Update

  • expect test now supports Unicode characters.

image

· 4 min read

MoonBit Update

  • inspect function added for the Show interface in expect testing with the following signature:
pub fn inspect(
obj : Show,
~content : String = "",
~loc : SourceLoc = _,
~args_loc : ArgsLoc = _) -> Result[Unit, String]

⚠️ This API is currently unstable and may be changed to the expect function in the future.

For example, in the following code:

fn add(x: Int, y: Int) -> Int {
x + y
}

test {
inspect(add(1, 2))?
}

test {
(add(3, 4) |> inspect)?
}

After executing moon test -u, the file is automatically updated to:

fn add(x: Int, y: Int) -> Int {
x + y
}

test {
inspect(add(1, 2), ~content="3")?
}

test {
(add(3, 4) |> inspect(~content="7"))?
}

  • Move compiler builtins to moonbitlang/core

Some fundamental MoonBit definitions, previously maintained inside the MoonBit compiler, is now migrated to moonbitlang/core

  • Alert pragma supported

MoonBit now supports writing multiple pragma in the top-level doc comments. All pragma start with @ and occupy a line by themselves.

Untitled

Currently, function and method alert pragma are supported. If functions or methods marked with alert are used, a warning will be generated. This mechanism can be used to mark functions as deprecated or unsafe. The format for alert pragmas is @alert id "explanation string", where id can be any identifier.

Untitled

  • Supported marking intrinsics in pragmas. For example, after the following code in the standard library was marked as intrinsic, the JavaScript backend would use the String(..) function to convert floating-point numbers into strings. We will add support for more functions as intrinsics in the future.
/// @intrinsic %f64.to_string
pub fn to_string(self : Double) -> String {
double_to_string(self)
}

  • Added ArgsLoc, used to automatically insert the position of each argument in the source code.
fn test_args_loc(a: Int, b: Int, ~args_loc : ArgsLoc = _) -> Unit {
println(args_loc)
}

fn init {
test_args_loc(1, 2)
// Output: [Some(path/to/xx.mbt:6:17-6:18), Some(path/to/xx.mbt:6:20-6:21), None]
}

  • Improved the completeness check for pattern matching.

  • Improved moonfmt

    • Adjusted the print format for function for loops; semicolons are no longer printed in special cases.
    • For cases of poor readability due to nested if/match, extra parentheses were added after formatting. The effects before and after formatting are as follows:

    Untitled

Untitled

  • Enhanced the performance and size of the generated code.
    • Introduced optimizations to eliminate local aliases, thus avoiding the generation of unnecessary local variables.
    • Introduced constant propagation optimizations.
    • Optimized the type declaration part of the generated wasm code, reducing redundant type declarations.
  • Adjusted string encoding to UTF-16.

IDE Update

  • The VSCode plugin supported installing or updating the MoonBit toolchain.

Untitled

Build System Update

  • Supported custom function export names in the moon.pkg.json file using the ["link"][BACKEND]["exports"] field, and by default, not all pub functions are exported anymore. It is now required to explicitly specify them in the exports. Additionally, linking non-main packages through the link field is now supported.

For example, creating a new project with moon new hello results in the following directory structure:

.
├── README.md
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── main
│ ├── main.mbt
│ └── moon.pkg.json
└── moon.mod.json

In the past, executing moon build would only generate a wasm file for the main package. Now, the link field in moon.pkg.json supports generating wasm files for non-main packages. The link field can be a boolean value:

{
"link": true // Indicates that the current package needs to be linked
}

Or an object, which allows setting link options for different backends like wasm or wasm-gc. Currently, only the exports option is supported. Exports is an array of strings that includes the functions to be exported and their names:

{
"link": {
"wasm": {
"exports": [
"hello" // This exports the function hello as hello
]
},
"wasm-gc": {
"exports": [
"hello:hello_wasm_gc" // This exports the function hello as hello_wasm_gc
]
}
}
}

If the content of lib/moon.pkg.json is modified to:

{
"link": {
"wasm": {
"exports": [
"hello"
]
}
}
}

Then executing moon build --output-wat, the output in target/wasm/release/build/lib/lib.wat will include the following content:

(func $username/hello/lib.hello.fn/1 (export "hello") (result i32)
(i32.const 10000))

Where (export "hello") indicates the configuration took effect.

· 2 min read

Build System Updates

  1. Added support for expect testing. a. Use moon new to create a new MoonBit project. b. Write in lib/hello.mbt:
pub fn hello() -> String {
"Hello, world!"
}

test {
let buf = Buffer::make(10)
buf.write_string(hello())
buf.expect()?
}

c. Then run moon test --update or moon test -u:

$ moon test --update
expect test failed at lib/hello.mbt:8:3-8:15
Diff:
----
Hello, world!
----

Total tests: 1, passed: 0, failed: 1.

Auto updating expect tests and retesting ...

Total tests: 1, passed: 1, failed: 0.

d. Reopen the lib/hello.mbt file, and you can see that the test result has been promoted to the source code.

pub fn hello() -> String {
"Hello, world!"
}

test {
let buf = Buffer::make(10)
buf.write_string(hello())
buf.expect(~content="Hello, world!")?
// ^~~~~~~~~~~~~~~~~~~~~~~~ Test result updated
}

  1. moon run no longer supports the output-wat option.

MoonBit Update

  1. Supported the backend code generation for multi-argument constructors. Now, when constructing a value of a generic type, if the generic parameter is a tuple, parentheses must be used to first explicitly construct a tuple, as in:

    enum A[X] {
    A(X)
    }

    fn init {
    // Error, expecting 1 arg, getting 2
    A::A(1, 2) |> ignore

    // Ok
    A::A((1, 2)) |> ignore
    }

    Multi-argument constructors unbox the parameters, which can improve the performance of the generated code and also allow programmers to have more control over the data's memory layout.

  2. Adjusted the lsl, lsr, asr methods of Int64, now the shift parameter is no longer Int64, but Int. Also adjusted the clz, ctz, popcnt methods, now their return type is no longer Int64, but Int. This change helps us generate more efficient code on platforms that do not support native Int64.

IDE Update

  1. Supported renaming of labeled arguments.
  2. The MoonBit VSCode plugin provides support for automatically installing or upgrading the MoonBit toolchain.

a. First, updating the plugin. If Moon is not installed or outdated, a notification will pop up to you to install or upgrade your MoonBit toolchain.

Untitled

b. Click "yes" to start the automatic installation task. When the task is completed, your MoonBit toolchain will be installed or upgraded to the latested version.

Untitled

· 3 min read

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

Language Update

1. Experimental support for default method in trait

trait MyShow {
repr(Self) -> String
str (Self) -> String // it has a default implementation
}

impl MyShow::str(self : Self) -> String {
// default implementation of str
self.repr()
}

type MyInt Int
fn repr(self:MyInt) -> String {
self.0.to_string()
}
// Now MyInt implements MyShow now

2. The type parameters of type definitions can be _, which can be used to define phantom types to restrict some logically illegal operations in the program. For example, we want to prevent adding lengths of different units together:

type Length[_] Int

type Kilometer
type Mile

fn add[T](x: Length[T], y:Length[T]) -> Length[T] {
Length::Length(x.0 + y.0)
}

let d_km: Length[Kilometer] = Length(10)
let d_mile: Length[Mile] = Length(16)

At this point, lengths with different units cannot be directly added:

fn init {
add(d_km, d_mile) |> ignore
// ^~~~~~ Error: Expr Type Mismatch
}

However, lengths with the same units can be added:

fn init {
add(d_km,d_km) |> ignore
// OK
}

3. Now, a top-level function without a marked return value is an error.

fn print_hello() {
// ^~~~~~~~~~~ Error:
// Missing type annotation for the return value.
println("Hello!")
}

4. Added the bitwise NOT operator.

fn main {
println((1).lnot())
}

Output:

-2

5. Improved the output of List::to_string/debug_write.

fn main {
let l = List::Cons(1, Cons(2, Cons(3, Nil)))
println(l)
}

Output:

List::[1, 2, 3]

6. Added the Byte type.

The byte literals are prefixed by b. The following snippet demonstrates its usage:

fn init {
let b1 = b'a'
println(b1.to_int())
let b2 = b'\xff'
println(b2.to_int())
}

More utility methods on Byte type are around the corner.

IDE Update

1. Added support for autocompletion of moonbitlang/core.

2. Formatting Update:

a. Adjust empty structs, enums, and traits to avoid blank lines.

Before:

struct A {

}

After:

struct A {}

b. Fixed incorrect indentation for continue c. Fixed issues with semicolons appearing after formatting multiline statements

Build System Update

1. Added the test_import field to moon.mod.json, which contains dependencies that are only used during testing.

2. Optimized the output of moon test; by default, it now only outputs information for failed test cases. Use the moon test -v command for complete output.

· 4 min read

MoonBit Update

  1. moonbitlang/core is now open-source at https://github.com/moonbitlang/core. We are thrilled to receive positive feedback from the community. To learn more about the moonbitlang/core open source details, click here: https://www.moonbitlang.com/blog/moonbitlang-core-opensource

  2. MoonBit now supports labeled argument and optional argument. Labelled arguments are useful for distinguishing different parameters of the same type in a function:

    fn greeting(~name: String, ~location: String) {
    println("Hi, \(name) from \(location)!")
    }

    fn init {
    greeting(~name="somebody", ~location="some city")
    let name = "someone else"
    let location = "another city"
    // `~label=label` can be abbreviated as `~label`
    greeting(~name, ~location)
    }

    Optional arguments must be labelled, and you must specify a default value when declaring an optional parameter. When the function is called, if no argument is explicitly provided, the default value will be used. Note: the default value expression will be evaluated on every function call:

    fn greeting(~name: String, ~location: Option[String] = None) {
    match location {
    Some(location) => println("Hi, \(name)!")
    None => println("Hi, \(name) from \(location)!")
    }
    }

    fn init {
    greeting(~name="A") // Hi, A!
    greeting(~name="B", ~location=Some("X") // Hi, B from X!
    }

  3. A new builtin type SourceLoc is added, representing source code location. If a function declares an optional parameter of type SourceLoc with _ as default value, MoonBit will automatically inject the location of call site when calling this function:

    fn f(~loc : SourceLoc = _) {
    println("called at \(loc)")
    }

    fn g(~loc : SourceLoc = _) {
    f() // show the position inside `g`
    f(~loc) // autofilled arguments can be manually overidden:
    // this call should show the location of the caller of `g`
    }

    test "source loc" {
    g()
    }

    Paste the above code into a new file called test.mbt at try.moonbitlang.com, and run the test "source loc", you should see the following output:

    test source loc ...
    called at memfs:/sample-folder/test.mbt:6:3-6:6
    called at memfs:/sample-folder/test.mbt:11:3-11:6

    SourceLoc can be used to write test utilities, providing handful location information when tests fail:

    fn assert_eq[X: Eq + Show](result: X, expect: X, ~loc : SourceLoc = _) -> Result[Unit, String] {
    if (result == expect) {
    Ok(())
    } else {
    Err("\(loc): Assertion failed: \(result) != \(expect)")
    }
    }

    test "1 =? 2" {
    assert_eq(1, 2)?
    }

    running 1 tests
    test 1 =? 2 ... FAILED memfs:/sample-folder/test.mbt:10:3-10:18: Assertion failed: 1 != 2

    test result: 0 passed; 1 failed

  4. === has been deprecated; please use physical_equal as its replacement.

  5. A new builtin type UnsafeMaybeUninit[T] and a few related builtin primitives have been added to support implementing high performance data structures, like vectors, in the MoonBit Core. Because they are unsafe, regular MoonBit programs are not expected to use them.

IDE Updates

  1. The online IDE now supports running tests via CodeLens.

  1. Significantly improved how moonfmt handles source code with comments.

  2. Enhanced stability and user experience for the IDE and VSCode plugin.

    a. The VSCode plugin now invokes moon check on file changes rather than starting moon check -w.

    b. Improved the typing experience for multi-line strings and docstrings. Now, inserting a line break inside a multi-line string/docstring automatically prefixes it with #| or ///.

    c. Fixed issues with hover, errors in moon.pkg.json, empty files, etc., that caused lsp errors.

Build System Update

Fixed several issues with moonbuild.

a. Fixed an issue where moon upgrade failed on Windows.

b. Fixed an issue where moon add did not remove the old version when adding a new version.

c. Fixed an issue where moon check could fail locally but still allow publishing.

Toolchain Update

Markdown linter now supports expr tags.

```moonbit expr
1 * 2 + 3
```

You can see the corresponding output when running mdlint:

5

· 4 min read

MoonBit Update

  1. We added support for += operators, including: +=, =, = and /=. You can use them to mutate the value of a mutable variable.


    fn init {
    let array = [1,2,3,4]
    array[2] *= 10
    println(array) // [1, 2, 30, 4]
    }

    fn init {
    let mut a = 1
    a += 20
    println(a) // 21
    }

    struct Foo {
    data : Array[Int]
    } derive(Debug)

    fn op_set(self : Foo, index : Int, value : Int) {
    self.data[index] = value
    }

    fn op_get(self : Foo, index : Int) -> Int {
    self.data[index]
    }

    fn init {
    let foo : Foo = { data: [0,0,0,0] }
    foo[2] -= 10
    debug(foo) // {data: [0, 0, -10, 0]}
    }

  2. Toplevel declarations that are not left-aligned will be an error now.

image|690x204

  1. New language feature: Super Trait. A super trait can be specified using :, e.g.

    trait A {
    // ...
    }

    trait B : A { // A is a super trait of B, B is a sub trait of A
    // ...
    }

    You can specify that a trait has multiple super traits, which means this trait requires those super traits to be implemented:

    // ...

    trait B: A + Compare + Debug {
    // ^~~ B is a sub-trait of A *and* Compare *and* Debug
    // ...
    }

    Wherever a trait is required, a sub-trait of that trait can also be used, but not the other way around. For built-in traits, we make Compare a sub-trait of Eq. This means if you have a value of type A : Compare, you can put it into a place where Eq trait is required. For example,

    trait Eq {
    op_equal(Self, Self) -> Bool
    }

    trait Compare: Eq {
    compare(Self, Self) -> Int
    }

    fn eq[X: Compare](this: X, that: X) -> Bool {
    this == that // Ok.
    }

    fn compare[X: Eq](this: X, that: X) -> Int {
    this.compare(that) // !! Error
    // ^~~~~~~ Type X has no method compare.
    }

  2. We addedT::[x, y, ...] as a short-hand notation for T::from_array([x, y, ...]), to provide a more handy way to initialize array-like structures, for example, a list.

    enum List[X] {
    Nil
    Cons(X, List[X])
    } derive(Show, Debug)

    fn List::from_array[X](array: Array[X]) -> List[X] {
    let mut list = List::Nil
    for i = array.length() - 1; i >= 0; i = i - 1 {
    list = Cons(array[i], list)
    }
    list
    }

    fn main {
    println(List::[1, 2, 3])
    // outputs Cons(1, Cons(2, Cons(3, Nil)))
    }

  3. fn hello() = "xx" has been deprecated. Instead, we suggest you write it as follows:

    extern "wasm" fn hello () =
    #| ...

    For now inline stubs like these only support wasmgc, not wasm1.

  4. The logic of derived Show implementation is modified. It now uses Debug as implementation. So to derive Show for a type, users now need to implement or derive Debug for that type first. Debug's output is valid MoonBit syntax for creating values, while Show can be used to produce better-looking output. This solves the problems of derived Show implementation with String fields:

    struct T {
    x: String
    } derive(Show, Debug)

    fn init {
    println({ x: "1, y: 2" })
    // before: {x: 1, y: 2}
    // now: {x: "1, y: 2"}
    }

  5. Dropping non-unit values is now an error. Use ignore to explicitly discard them.

    fn f() -> Int {
    ignore(3) // Ok.
    3 |> ignore // Ok.
    3 // Err: Expr Type Mismatch: has type Int, wanted Unit
    3 // Ok, as this value is returned, not dropped
    }

  6. test is now a keyword, and cannot be used as an identifier anymore.

IDE Update

  1. Better support for Markdown in online IDE.
  • MoonBit Code inside markdown will be highlighted.

image|682x500

Build System Update

  1. We support the main function now. However, there are some caveats you might want to know
    1. It can only be in the main package (where is_main: true).
    2. The main package should have exactly one main function.
    3. It will be executed after all init functions
    4. We disallow any testto co-exists with the main function inside the same package.
  2. You can use moon upgradeto upgrade your MoonBit toolchain to the latest version. However, before using it, you must run the installation script one more time :-)
  3. The moon check|build|run command now defaults to linking with the standard library moonbitlang/core.

· 4 min read

MoonBit Update

1. Supports cloud-native debugging features.

Now, you can debug MoonBit programs directly in your browser using devtools by visiting try.moonbitlang.com, without installing any software. The steps are as follows:

2. MoonBit now supports functional loop control flow defined with the for keyword.

MoonBit now supports functional loop control flow defined with the for keyword, purely functional yet as efficient as C. For instance, the fib function can be written as follows:

fn fib( n : Int ) -> Int {
for i = 0, a = 1, b = 2
i < n
i = i + 1, a = b, b = a + b {
} else { b }
}

MoonBit's for loop can return a value as an expression. For example, in the program mentioned above, b is used as the value of the entire for loop after the loop ends. It's also possible to return early by using break within for loop body, like:

fn exists(haystack: Array[Int], needle: Int) -> Bool {
for i = 0; i < haystack.length(); i = i + 1 {
if haystack[i] == needle {
break true
}
} else {
false
}
}

Moreover, within the for loop, you can use continue to proceed to the next iteration, just as in traditional languages. MoonBit further offers a parameterized continue to specify the value of the loop variable in the next iteration of the loop, for example:

fn find_in_sorted[T](xs: Array[(Int, T)], i: Int) -> Option[T] {
for l = 0, r = xs.length() - 1; l < r; {
let mid = (l + r) / 2
let k = xs[mid].0
if k == i {
break Some(xs[mid].1)
} else if k > i {
continue l, mid
} else {
continue mid + 1, r
}
} else {
None
}
}

In cases where a return value is not required, the else branch can be omitted, like:

fn print_from_0_to(n: Int) {
for i = 0; i <= n; i = i + 1 {
println(i)
}
}

3. Inline Test Improvements

The return type of tests has been changed from Unit to Result[Unit, String] to represent the outcome of the test:

test "id" {
if (id(10) != 10) { return Err("The implementation of `id` is incorrect.") }
}

The compiler automatically wraps the block {...} in a test "id" {...} statement with Ok(). Therefore, when the block type is Unit and there is no early return, it indicates that the inline test has passed. Combined with the question mark operator, this makes testing more elegant:

fn id(x : Int) -> Int {
x + 1 // incorrect result
}

fn assert(x : Bool, failReason : String) -> Result[Unit,String] {
if x { Ok(()) } else { Err(failReason) }
}

test "id" {
assert(id(10) == 10, "The implementation of `id` is incorrect.")?
}

Running moon test, generates the following output:

➜  my-project moon test
running 1 tests in package username/hello/lib
test username/hello/lib::hello ... ok

test result: 1 passed; 0 failed

running 1 tests in package username/hello/main
test username/hello/main::id ... FAILED: The implementation of `id` is incorrect.

test result: 0 passed; 1 failed

Hello, world!

4. Improved function signature hints in the VS Code extension, now displaying parameter names:

5. Enhanced support for core package development in the VS Code extension.

6. moon new supports the rapid creation of new projects.

  • moon new hello creates an executable project named username/hello inside the hello folder.

  • moon new hello --lib creates a library named username/hello inside the hello folder.

· 2 min read

MoonBit Update

  1. Added support for functional for loop control flow. Unlike traditional imperative for loops, the loop variable is immutable. Such a design will also be easier to extract for formal verification in the future.
fn init {
for i = 0; i < 5; i = i + 1 {
debug(i)
// i = i + 4 error: The variable i is not mutable.
}
}

Output:

01234

The functional for loop also supports multiple bindings. Setting it apart from other languages, x and y have semantics of being updated simultaneously in the third expression of the functional for loop:

fn init {
for x = 0, y = 0; x < 10; x = x + 1, y = x + 1 {
// ^~~ the value of x is pre-update
println("x: \(x), y: \(y)")
}
}

Output:

x: 0, y: 0
x: 1, y: 1
x: 2, y: 2
x: 3, y: 3
x: 4, y: 4
x: 5, y: 5
x: 6, y: 6
x: 7, y: 7
x: 8, y: 8
x: 9, y: 9

Functional for loop also supports the use of break and continue.

fn init {
let xs = [0,1,2,3,4,5,6,7,8]
let mut sum = 0
for i = 0, v = xs[0]; i < xs.length(); i = i + 1, v = xs[i + 1] {
if v % 2 == 0 { continue }
if v >= 7 { break }
sum = sum + v
}
debug(sum) //output: 9
}
  1. Improved the wizard for creating projects with moon new, now allowing the selection of creating a lib or an exec project using the arrow keys:

  1. The IDE now supports intelligent autocompletion for the pipeline operator. Functions whose first parameter type matches the type of the expression on the left side of the pipeline will be placed at the top of the completion list, while other completion options will still be displayed further down the list.

  1. Adjusted the pipe expression based on community feedback. Now, the right side of the pipeline operator supports function calls like Double::to_int.
fn init {
debug(3.14 |> Double::to_int) // output: 3
debug(4 |> Array::make('c')) // output: ['c', 'c', 'c', 'c']
}
  1. Fixed an issue in the IDE where inlay hints were incorrectly inserted for infix expressions.

· One min read

MoonBit Update

  1. Introduced support for multi-line strings, requiring each line to commence with #|. Multi-line strings allow for breaks and comments between lines, but do not support escape sequences or string interpolation.

  1. Functional loop: A functional style loop. Here, continue is only allowed at the position of tail recursion calls, and within the loop, break can be used to return a value early.

  1. Added support for Trait::method calls: Enables calling trait methods in the form of Debug::debug_write(self, buf).

  1. Supported implicit conversion to trait objects. When a trait object is explicitly required in the context, as SomeTrait will be automatically inserted. For example, in the code below:

Now we can omit as Debug.

  1. Supported inlay hints for function parameters.

  1. Strings and character literals now support Unicode escapes, hexadecimal escapes, and octal escapes.

· 2 min read

MoonBit Update

1.New feature ——Trait object

It can explicitly box values of different types but implement the same trait and represent them as the same type, achieving dynamic dispatch of functions.

fn get_show_list() -> List[Show] {
let a = 42 as Show
let b = "xxx" as Show
let c = 3.14 as Show
List::Cons(a, Cons(b, Cons(c, Nil)))
}

fn init {
fn print_show_list {
List::Cons(a, rest) => { println(a); print_show_list(rest) }
List::Nil => ()
}
print_show_list(get_show_list())
}

2. Introduction of the Pipe Operator

The Pipe Operator, provides a syntax similar to method chaining, which can string together multiple consecutive function calls, eliminating the need forlet name = ... code. For example, value |> func1(arg1,arg2) |> func2 is equivalent to:

let a = value
let b = func1(a, arg1, arg2)
func2(b)

Another example:

fn sub2(x : Int, y : Int) -> Int {
x - y
}

fn sum3(x : Int, y : Int, z : Int) -> Int {
x + y + z
}

fn init {
6 |> sub2(5) |> sum3(1,2) |> println()
}

3. Strings support hexadecimal escape using \xFF

fn init {
let data = "\x48\x65\x6c\x6c\x6f"
println(data) //output: Hello
}

4. Inline test change

Now test mode will also run fn init, and the order of execution is before inline test.

5. Moonfmt: Improved indentation of types and long array literals.

Original code:

Formatting before improvements:

Formatting after improvements: