Skip to main content

Weekly 2024-09-03

Β· 5 min read

Language Updates​

  • Breaking Change: String interpolation syntax changed from \() to \{} where more expression types within \{} are supported. For example:
fn add(i: Int) -> Int {
i + 1
}

fn main {
println("\{add(2)}") // cannot invoke function in \() before
}
  • New Optional Argument: Supported a new optional argument syntax where the compiler automatically inserts the Some constructor. Optional arguments of type Option are common, with a default value of None, and explicitly passed arguments use the Some constructor. The new syntax ~label? : T simplifies this process:
fn image(~width?: Int, ~height?: Int, ~src: String) -> Unit

Previously, we had to manually insert lots of Some constructors with the default value, which is quite troublesome.

image(width=Some(300), height=Some(300), src="img.png")

Therefore, MoonBit introduced a new optional argument syntax ~label? : T, which is quite similar to ? in map pattern:

fn image(~width?: Int, ~height?: Int, ~src: String) -> Unit

At this point, width and height are optional arguments with a default value of None, and their types in the function body are Int?. When calling image, if you want to provide values for width and height, you don't need to manually insert Some; the compiler will automatically do it for you:

image(width=300, height=300, src="img.png")

If you need to pass an Int? type value directly to image, you can use the following syntax:

fn image_with_fixed_height(~width? : Int, ~src : String) -> Unit {
// `~width?` is shorthand for `width?=width`
image(~width?, height=300, src="img.png")
}
  • Range Operators: Added support for range operators ..< (upper bound exclusive) and ..= (upper bound inclusive) to simplify for loops:
fn main {
for i in 1..<10 {
println(i)
}
}

Currently, the range operators only support the built-in types Int, UInt, Int64, and UInt64, and they can only be used within for .. in loops. In the future, these limitations may be relaxed.

  • expr._ replaces <expr>.0: Introduced expr._ syntax for accessing newtypes. The previous syntax <expr>.0 will be deprecated in the future, and currently, it will trigger a warning. The purpose of this change is to simplify the usage of newtype when wrapping a struct or tuple type by automatically forwarding field access to the underlying type, making it easier to use newtype.

  • Trait Implementation Consistency: Implementing a trait now includes consistency checks to ensure that all impl signatures match. For example:

trait MyTrait {
f1(Self) -> Unit
f2(Self) -> Unit
}

// The signatures of these two `impl` blocks are inconsistent,
// with `f2` being more generic. However, because `impl` is used
// to implement a specific trait, this generality in `f2` is meaningless.
// All `impl` blocks for the same trait and type should have consistent signatures.
impl[X : Show] MyTrait for Array[X] with f1(self) { .. }
impl[X] MyTrait for Array[X] with f2(self) { ... }

Core Update​

  • Deprecate xx_exn: Functions named xx_exn have been renamed to unsafe_xx (e.g., unsafe_pop, unsafe_nth, unsafe_peek).

  • Breaking Change: Converting floating-point numbers to strings now conforms to the ECMAScript standard.

  • Function Update: The op_as_view function type signature has been updated for compatibility with the new optional argument syntax.

Before:

fn op_as_view[T](self : Array[T], ~start : Int, ~end : Int) -> ArrayView[T]

Now:

fn op_as_view[T](self : Array[T], ~start : Int, ~end? : Int) -> ArrayView[T]

This allows the Iter type to implement the op_as_view method, enabling slice syntax:

fn main {
let it: Iter[Int] = [1, 2, 3, 4].iter()[1:2] // slice syntax on iter

for x in it {
println(x) // 2
}
}

As the new optional argument syntax ~end? : Int is backward compatible, all previous ways of calling op_as_view still work and maintain the same semantics.

  • Renaming: Int::to_uint and UInt::to_int have been renamed to Int::reinterpret_as_uint and UInt::reinterpret_as_int.

  • Removal and Fixes: The BigInt::lsr function was removed, and bug fixes and performance improvements were made to BigInt.

Toolchain Updates​

  • Breaking Change: The Diagnostic information text for moon {build,check,test,run} (such as printed errors and warnings) has been moved from stdout to stderr to avoid pollution of stdout output with error and warning details when running moon {test,run}. If your tools rely on stdout text-format diagnostics, please update your code accordingly.

    JSON mode output is unaffected.

  • MoonBit AI: Supports batch generation of tests and documentation.

ai-package

  • New Feature: Snapshot testing is now supported, similar to inspect!, but results are written to a file. For example, when running moon test -u, the following test block will generate a file 001.txt in the __snapshot__ folder:
test (it : @test.T) {
it.write(".")
it.writeln("..")
it.snapshot!(filename="001.txt")
}

Note that snapshot testing ignores LF and CRLF differences.

  • Build Process Update: moon build now supports building projects even without is-main or link fields in moon.pkg.json. These packages will only generate a core file without linking to wasm/js.

  • Formatting Update: moon fmt now supports incremental formatting, initially formatting all .mbt files, then only formatting changed files in subsequent runs.

IDE Updates​

  • Project-Level Goto References: The IDE now supports project-level goto references. For example, you can find all references to a function like inspect within the core. In the pic, all references calling inspect are found:

reference

  • Test Block Debugging: Test blocks allows for quick debugging through codelens.

codelens

weekly 2024-08-19

Β· 2 min read

Language Update​

  • MoonBit Beta Preview

MoonBit Beta Preview achieved major features of modern generic system, precise error handling, and efficient iterators, and an AI-powered toolchain, offering real use cases in cloud and edge computing. Read our latest blog for details.

  • Error Type Printing Improvement

Error type printing now allows error types that implement Show to display more detailed information when used as Error. For example:

type! MyErr String derive(Show)
fn f() -> Unit! { raise MyErr("err") }
fn main {
println(f?()) // Output: Err(MyErr("err"))
}
  • Test Block Enhancement

Added support for parameters in test blocks. The parameter type must be @test.T.

test "hello" (it: @test.T) {
inspect!(it.name, content="hello")
}

Build System Update​

  • Fixed an issue where moon info would write the wrong mbti file path when the source field was set in moon.mod.json.

Core Update​

  • Added last function to array and fixedarray.

  • Frequently used functions in the test package have been moved to builtin, and the old functions have been deprecated.

  • Added last, head, and intersperse methods to iter.

IDE Update​

  • MoonBit IDE now supports the VS Code Test Explorer.

test explorer

  • Online IDE Multi-Package Editing

The online IDE now supports multi-package editing. You can develop MoonBit modules with package dependencies just like in a local environment. In this example, the main package depends on the lib package.

IDE

Other Update​

  • MoonBit docs domain is now: https://docs.moonbitlang.com.

  • As MoonBit's language features stabilize and reaches the beta preview, update announcements will be adjusted to once every two weeks.

weekly 2024-08-12

Β· 5 min read

Language Update​

  • Added support for for .. in loops based on Iter and Iter2 types:

    fn main {
    for x in [1, 2, 3] {
    println(x)
    }
    for k, v in {"x": 1, "y": 2} {
    println("\{k} => \{v}")
    }
    }

    You can bind one or two variables between for and in to iterate over elements in Iter. A single-variable loop for x in expr1 iterates over expr1.iter() : Iter[_], while a two-variable loop for x, y in expr2 iterates over expr2.iter2() : Iter2[_, _]. Underscores can be used to ignore elements, but pattern matching is not allowed between for and in.

    The loop body in for .. in can use control flow statements like return/break/raise:

    test "for/in" {
    // `iter2` for arrays iterates over index + elements
    for i, x in [1, 2, 3] {
    assert_eq!(i + 1, x)
    }
    }

-Introduced new string interpolation syntax \{}, deprecating the old \(). This allows embedding more complex expressions directly into strings. Future updates will relax syntax restrictions within string interpolations, such as supporting \{1 + 2} and \{x.f(y)}.

"hello, \(name)!" // warning: deprecated
"hello, \{name}!" // new syntax
  • Expanded numerical handling: Added a new built-in BigInt type for managing large values beyond the range of regular integers.
// BigInt literals end with N
let num = 100000000N

// Like Int literals, underscores are allowed between digits. Hexadecimal, octal, and binary formats are also supported.
let n2 = 0xFFFFFFFFN
let n3 = 0o77777777N
let n4 = 0b1111111100000000N
let n5 = 1234_4567_91011N

// If the type is explicitly BigInt, the N suffix is not required
let n6 : BigInt = 1000000000000000000

// Pattern matching also supports BigInt
match 10N {
1N => println(1)
10N => println(10)
100 => println(100)
}
  • Added support for declaring error types using enums. For example, errors in the Json package can be declared as follows:

    pub type! ParseError {
    InvalidChar(Position, Char)
    InvalidEof
    InvalidNumber(Position, String)
    InvalidIdentEscape(Position)
    } derive(Eq)

    You can also use labeled and mutable arguments within error types using enums, the same as how you would use them in regular enum types.

type! Error1 {
A
B(Int, ~x: String)
C(mut ~x: String, Char, ~y: Bool)
} derive(Show)

fn f() -> Unit!Error1 {
raise Error1::C('x', x="error2", y=false)
}

fn g() -> Unit!Error1 {
try f!() {
Error1::C(_) as c => {
c.x = "Rethrow Error2::C"
raise c
}
e => raise e
}
}

fn main {
println(g?()) // Err(C(x="Rethrow Error2::C", 'x', y=false))
}
  • Introduced catch! syntax to rethrow errors that are not handled within an error handler. By using catch!, you can more easily manage and propagate errors through your code, simplifying the process of error handling. For example, the function g above can be rewritten as:
fn g() -> Unit!Error1 {
try f!() catch! {
Error1::C(_) as c => {
c.x = "Rethrow Error2::C"
raise c
}
}
}
  • The generated JavaScript code no longer relies on the TextDecoder API. If Node.js support for TextDecoder improves in the future, we might consider adding it back.

IDE Update​

  • Fixed an issue where the source code in the core library couldn't load in web-based VSCode plugin while debugging. Debugging features in MoonBit IDE are now functional.

  • MoonBit IDE now supports auto-completion for constructors in pattern matching based on type:

match (x : Option[Int]) {

// ^^^^ Auto-completes `None` and `Some`
}

Core Update​

  • Added a new Iter2 type for iterating over collections with two elements, like Map, or iterating over arrays with an index:
fn main {
let map = {"x": 1, "y": 2}
map.iter2().each(fn (k, v) {
println("\{k} => \{v}")
})
}

Compared to Iter[(X, Y)], Iter2[X, Y] offers better performance and native support for for .. in loops.

  • Moved @json.JsonValue to the @builtin package and renamed it to Json. @json.JsonValue is now an alias for Json, so this change is backward compatible.

  • Added a ToJson interface in @builtin to represent types that can be converted to Json.

Build System Update​

  • Added -C/--directory commands to moon check|build|test, equivalent to --source-dir, to specify the root directory of a MoonBit project, i.e., where moon.mod.json is located.

  • Updated the root-dir in moon.mod.json to source. This field specifies the source directory for modules, and the value of the source field can be a multi-level directory but must be a subdirectory of the directory containing moon.mod.json, e.g., "source": "a/b/c".

    This field is introduced because package names in MoonBit modules are related to file paths. For example, if the current module name is moonbitlang/example and a package is located at lib/moon.pkg.json, you would need to import the package using its full name moonbitlang/example/lib. Sometimes, to better organize the project structure, we may want to place the source code in the src directory, such as src/lib/moon.pkg.json. In this case, you would need to use moonbitlang/example/src/lib to import the package. However, generally, we do not want src to appear in the package path, so you can specify "source": "src" to ignore this directory level and still import the package as moonbitlang/example/lib.

Toolchain Update​

  • MoonBit AI supported generating code explanations: In MoonBit IDE, click the MoonBit logo and select /explain to get code explanations which will appear on the right side. Don't forget to click πŸ‘ or πŸ‘Ž to give us your feedback.

ai explain

weekly 2024-08-05

Β· 5 min read

Language Update​

  • JSON Literal Support for Array Spread:
let xs: Array[@json.JsonValue] = [1, 2, 3, 4]
let _: @json.JsonValue = [1, ..xs]
  • Type Alias Support: Added support for type aliases, primarily for gradual code refactoring and migration rather than just giving types short names. For example, if you need to rename @stack.Stack to @stack.T, doing it all at once would require modifying many places that use @stack.Stack, which could easily cause conflicts in large projects. If a third-party package uses @stack.Stack, it would result in a breaking change. With type alias, you can leave an alias for @stack.Stack after renaming it, so existing code won't break:
/// @alert deprecated "Use `T` instead"
pub typealias Stack[X] = T[X]

Then, you can gradually migrate the usage of @stack.Stack, giving third-party users time to adapt to the new name. Once the migration is complete, you can remove the type alias. Besides type renaming, typealias can also be used for migrating type definitions between packages, etc.

  • Support for Defining New Methods on Trait Objects:
trait Logger {
write_string(Self, String) -> Unit
}

trait CanLog {
output(Self, Logger) -> Unit
}

// Define a new method `write_object` for the trait object type `Logger`
fn write_object[Obj : CanLog](self : Logger, obj : Obj) -> Unit {
obj.output(self)
}

impl[K : CanLog, V : CanLog] CanLog for Map[K, V] with output(self, logger) {
logger.write_string("Map::of([")
self.each(fn (k, v) {
// Use `Logger::write_object` method for simplification
logger
..write_string("(")
..write_object(k)
..write_string(", ")
..write_object(v)
.write_string(")")
})
logger.write_string("])")
}
  • [Breaking Change] Error Type Constraint: In the return type T!E that may return errors, the error type E must be a concrete error type declared with the type! keyword. Currently, two declaration methods are supported:
type! E1 Int   // error type E1 has one constructor E1 with an Integer payload
type! E2 // error type E2 has one constructor E2 with no payload

In function declarations, you can use these concrete error types for annotations and return specific errors using raise, for example:

fn f1() -> Unit!E1 { raise E1(-1) }
fn f2() -> Unit!E2 { raise E2 }
  • Default Error Type: Added a built-in Error type as the default error type. Functions can use the following equivalent declarations to indicate they may return an Error type error:
fn f1!() -> Unit { .. }
fn f2() -> Unit! { .. }
fn f3() -> Unit!Error { .. }

For anonymous functions and matrix functions, you can use fn! to indicate the function may return an Error type error, for example:

fn apply(f: (Int) -> Int!, x: Int) -> Int! { f!(x) }

fn main {
try apply!(fn! { x => .. }) { _ => println("err") } // matrix function
try apply!(fn! (x) => { .. }) { _ => println("err") } // anonymous function
}

Errors returned using raise and f!(x) can be cast up to the Error type, for example:

type! E1 Int
type! E2
fn g1(f1: () -> Unit!E1) -> Unit!Error {
f1!() // error of type E1 is cast to Error
raise E2 // error of type E2 is cast to Error
}

Error types can be pattern matched. When the matched type is Error, pattern matching completeness checks require adding a branch using the _ pattern, whereas this is not needed for specific error types, for example:

type! E1 Int
fn f1() -> Unit!E1 { .. }
fn f2() -> Unit!Error { .. }
fn main {
try f1!() { E1(errno) => println(errno) } // this error handling is complete
try f2!() {
E1(errno) => println(errno)
_ => println("unknown error")
}
}

In addition, if different kinds of error types are used in a try expression, the entire try expression will be handled as returning the Error type, for example:

type! E1 Int
type! E2
fn f1() -> Unit!E1 { .. }
fn f2() -> Unit!E2 { .. }
fn main {
try {
f1!()
f2!()
} catch {
E1(errno) => println(errno)
E2 => println("E2")
_ => println("unknown error") // currently needed to ensure completeness
}
}

We will improve this in future versions to make completeness checks more precise.

  • Error Bound: Added Error bound to constrain generic parameters in generic functions, allowing them to appear as error types in function signatures, for example:
fn unwrap_or_error[T, E: Error](r: Result[T, E]) -> T!E {
match r {
Ok(v) => v
Err(e) => raise e
}
}

Core Update​

  • Bigint: Changed Bigint to a built-in type.

Build System Update​

  • Debug Single .mbt File: Added support for debugging a single .mbt file.

  • Parallel Package-Level Testing: moon test now supports parallel testing at the package level.

  • root-dir Field in moon.mod.json: Added root-dir field to specify the source directory of the module. Only supports specifying a single-level folder, not multi-level folders. moon new will default to setting root-dir to src. The default directory structure for exec and lib modes is now:

exec
β”œβ”€β”€ LICENSE
β”œβ”€β”€ README.md
β”œβ”€β”€ moon.mod.json
└── src
β”œβ”€β”€ lib
β”‚ β”œβ”€β”€ hello.mbt
β”‚ β”œβ”€β”€ hello_test.mbt
β”‚ └── moon.pkg.json
└── main
β”œβ”€β”€ main.mbt
└── moon.pkg.json

lib
β”œβ”€β”€ LICENSE
β”œβ”€β”€ README.md
β”œβ”€β”€ moon.mod.json
└── src
β”œβ”€β”€ lib
β”‚ β”œβ”€β”€ hello.mbt
β”‚ β”œβ”€β”€ hello_test.mbt
β”‚ └── moon.pkg.json
β”œβ”€β”€ moon.pkg.json
└── top.mbt

Toolchain Update​

  • MoonBit AI: Now supports generating documentation.

ai file

weekly 2024-07-29

Β· 3 min read

Language Updates​

  • Simplified Error Handling Syntax: The syntax for capturing potential errors in function calls has been updated from f(x)!! to f?(x), which returns a Result type.
fn div(x: Int, y: Int) -> Int!String {
if y == 0 {
raise "division by zero"
}
x / y
}

fn main {
let a = div?(10, 2)
println(a) // Ok(5)
}
  • JSON Literal Support: Added support for JSON literals. Numbers, strings, arrays, and dictionary literals are now overloaded to provide a JSON type, which can be used for constructing/matching JSON when the type is expected.
fn json_process(x: @json.JsonValue) -> Double {
match x {
{
"outer": {
"middle": {
"inner": [
{ "x": Number(x) },
{ "y": Number(y) }
]
}
}
} => x + y
_ => 0
}
}

fn main {
let x: @json.JsonValue = {
"outer": { "middle": { "inner": [{ "x": 42 }, { "y": 24 }] } },
"another_field": "string value",
}
json_process(x) |> println
}
  • Derive Hash Support: Added derive(Hash) to automatically generate implementations of the Hash trait for types.
enum MyType {
C1
C2(Int)
} derive(Eq, Hash)

fn main {
let m = {}..set(C1, 1)..set(C2(1), 2)..set(C2(2), 3)
println(m[C1]) // Some(1)
println(m[C2(1)]) // Some(2)
println(m[C2(3)]) // None
}
  • JavaScript Backend Update: Removed dependency on TextDecoder in generated code.

Core Updates​

  • Function Signature Changes:

    • Array::new_with_index -> Array::makei
    • FixedArray::new / FixedArray::new_with_index -> FixedArray::makei
    • FixedArray::empty -> FixedArray::new
  • Removed Debug Trait

Build System Updates​

  • Single .mbt File Support: Now supports running a single .mbt file (not within a package). Usage: moon run xx.mbt (links to the standard library by default, requires a main function in xx.mbt).

  • Shell Completion: Moon now supports shell completion.

    Example Usage: Zsh

    Zsh automatically reads all scripts under $FPATH on startup. You can place the completion script in any path under $FPATH.

    To manage more conveniently, create a new path for completions $ZSH/completions and add it to $FPATH.

    mkdir $ZSH/completions
    echo "FPATH+=$ZSH/completions" >> ~/.zshrc
    moon shell-completion > $ZSH/completions/_moon
    . ~/.zshrc # Reload to use, or omz reload

    To maintain portability, you can directly add the following line to .zshrc:

    eval "$(moon shell-completion --shell=zsh)"

    shell-completion.gif

    For the effect shown in the pic, you also need to install the zsh-autocomplete and zsh-autosuggestions plugins.

Toolchain Updates​

  • moonfmt Fixes:

    Fixed an issue with array spread formatting adding braces.

    Adjusted parenthesis inference for pattern matching to avoid using wildcards.

  • VS Code Auto-test Support: Now supports automatic test generation for functions, including black-box tests (ending in _test.mbt) and white-box tests (unit tests).

  • Run | Debug Buttons for .mbt: Added support for single .mbt file Run | Debug buttons.

weekly 2024-07-22

Β· 6 min read

Language Updates​

Breaking Changes​

  • Error Handling Syntax: The syntax for error handling has changed from f(x)! to f!(x). This change facilitates IDE auto-completion, allowing the function name and ! to be completed together and enabling special rendering for f!.

  • Local Functions: If a local function can return an error type, it must be annotated. For example:

fn toplevel() -> Unit!String {
fn local1() -> Unit!String { raise "err" } // local function
fn local2() -> _!_ { raise "err" } // local function
apply!(fn (_x) -> Int!String { raise "err" }, 42) |> ignore // anonymous function
}

fn apply(f: (Int) -> Int!String, x: Int) -> Int!String {
f!(x)
}

Additionally, the fn! and fn local!() syntax can be used to indicate that a function might return an error and to let the compiler infer the error type. The former is used for lambda and anonymous functions, and the latter for regular local functions. For example:

fn toplevel() -> Unit!String {
fn local!() { raise "err" } // local function
apply!(fn! (_x) { raise "err" }, 42) |> ignore // anonymous function
apply!(fn! { _ => raise "err" }, 42) |> ignore // lambda function
}

Try Expression Enhancements​

The try expression has been improved. The complete usage of the try expression is as follows:

fn test_try() -> Unit {
fn f() -> Int!String {
raise "err"
}
try {
println("this is try body")
f!()
} except {
err => println(err)
} else {
val => println(val)
}
}
  • The except keyword is used to handle errors returned by the try body.
  • The else keyword is used to handle normal returns from the try body.

Simplified cases for the try expression:

  • The else branch can be omitted if no handling of the return value is needed.
  • The try body can be a simple expression.
  • The except keyword can be omitted.

For example:

fn test_try() -> Result[Int, String] {
fn f() -> Int!String {
raise "err"
}
try Ok(f!()) { err => Err(err) }
}

Integer Literal Overloading in Pattern Matching​

Integer literal overloading has been added to pattern matching:

fn is_42(x : Double) -> Bool {
match x {
42 => true
_ => false
}
}

Upcoming Breaking Changes​

  • Map Pattern Matching: The semantics of { "key" : value } in map patterns will be changed this week.

    Previously, in a map pattern, value was of type Option[_] and would match regardless of whether "key" existed.

    In the future, { "key": value } will match only if "key" exists, and value will directly match the actual content of "key".

    To match both the presence and absence of "key", use the new syntax { "key"? : value_or_none }, where value_or_none is of type Option[_].

  • Trait Implementation Syntax: The old syntax for explicit trait implementation fn Trait::method(...) will be removed this week. Existing code should use the impl Trait for SelfType with method(...) syntax. The fn Trait::method(...) syntax will be repurposed for defining methods on trait object types in the future.

IDE Updates​

  • Run | Debug Codelens for the main function: The main function will now have a Run | Debug Codelens for quick execution and debugging. Currently, quick debugging only supports the JavaScript backend.

ide

  • AI-Generated Tests for Top-Level Functions: Top-level functions now support generating tests through AI via Codelens. * Please note that this feature is still under development and may not be stable.

    1. Click the "Gen test" button.

    step1

    1. A new window will appear on the right side, displaying the generated test.

    step2

    1. Click "Reject" to remove unwanted tests, or "Insert" to add the test into the source file.

Core Updates​

  • Show Trait Redefinition: The Show trait has been redefined for more accurate behavior. The new definition of the Show trait is as follows:
pub trait Show {
// The `output` function is for implementing `Show` for composite types.
// It writes a string representation of `Self` to a `Logger`.
output(Self, Logger) -> Unit
// The `to_string` method is for `Show` users.
// It can be used for string interpolation and `println`.
// `to_string` has a default implementation using `output`.
// Some types, like `String`, will override `to_string` to modify its behavior.
to_string(Self) -> String
}

The new Show resolves the incorrect behavior of String. Previously, if a composite type contained a String, the Show implementation would not escape this String, leading to incorrect results. Conversely, String used directly in string interpolation and println should not be escaped. The new Show definition and its core implementation address both needs.

For Show implementers, please keep in mind:

  • Implement the output method, not the to_string method.

  • When recursively calling the Show implementation of substructures (e.g., struct fields), use Show::output(..., logger) instead of to_string.

  • The default implementation of Show::to_string is not a method, so it cannot be called with a dot. To support .to_string() syntax, add a helper definition like this:

pub fn to_string(self: ...) -> String {
Show::to_string(self)
}
  • Upcoming Breaking Change: Deprecation of Debug Trait: Following the redefinition of the Show trait, the functionality of the Debug trait is now completely covered by Show. Therefore, the Debug trait is deprecated and will be removed soon.

  • Data Structure Type Renaming: Data structure type names have been changed to T. For example:

// old
// let m : @hashmap.HashMap[Int, Int] = @hashmap.new()

// new
let m : @hashmap.T[Int, Int] = @hashmap.new()

The following data structures have been renamed to T:

  • deque
  • hashmap
  • hashset
  • immut/array
  • immut/hashmap
  • immut/hashset
  • immut/list
  • immut/priority_queue
  • immut/sorted_map
  • immut/sorted_set
  • queue
  • priorityqueue
  • rational
  • sorted_map
  • sorted_set

Build System Updates​

  • Build Output Adjustment: The build process output has been adjusted to only print a command if that command fails.

  • Target Specification for moon check|build|test|bundle: Previously, the --target option only supported three choices: wasm|wasm-gc|js. Now it supports specifying multiple backends to run in parallel. The supported options have changed to wasm|wasm-gc|js|all, and they can be combined with commas. The --serial option changes the parallel execution of different backends to serial execution, but the same backend will still execute concurrently. For example:

    • moon test --target all runs tests for all backends.
    • moon test --target js,wasm runs tests for js and wasm backends.
    • moon test --target js,wasm --serial is equivalent to running moon test --target js; moon test --target wasm sequentially.
  • Memory Configuration in moon.pkg.json: Added heap-start-address and import-memory configuration for ["link"]["wasm"|"wasm-gc"] in moon.pkg.json:

{ 
"link": {
"wasm": {
"heap-start-address": 65536,
"import-memory": {
"module": "xxx",
"name": "yyy"
}
}
}
}
  • Default Protocol for moon new: The default protocol for moon new has been changed to Apache-2.0.

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.