20260513 MoonBit v0.9.2 Release
moonc version: v0.9.2+bbe2b338f
Language Updatesβ
-
New list comprehension syntax:
// Use `if` to filter elements let even_numbers = [ for i in 0..<100 if i % 2 == 0 => i ] // When creating an `Iter`, you can create infinite sequences let fib_numbers : Iter[Int] = [ for p1 = 1, p2 = 0;; p1 = p1 + p2, p2 = p1 => p1 ]The syntax is
[ for .. => <body> ]. An optionalif <guard>can be added before=>to filter elements. Theispattern can be used inside the guard to bind variables, and these bound variables can be used in the body. All forms offorloops, includingfor .. inloops and regularforloops, can be used in list comprehensions.List comprehensions can construct all built-in array-like types, including
Array/FixedArray/ReadOnlyArray/Bytes/Stringand their corresponding view types. In addition, list comprehensions can be used to constructItertypes, in which case the entire sequence is lazily evaluated β elements are only computed when theIterreaches the corresponding element, allowing infinite sequences to be created. The specific type constructed by a list comprehension expression is determined by the context type, and can be explicitly controlled via type annotation.The body of a list comprehension currently does not support any control flow operations, including
return/break/continue, throwing errors, or async operations. -
Support constructing
Itertypes from array literals.Now, when an array literal's expected type can be
Iter, it becomes an iterator containing the corresponding elements. Elements in the literal are still evaluated immediately when constructing the literal, rather than being wrapped in anIterfor lazy evaluation. Therefore, the evaluation order of array literals does not implicitly change with the type. -
Adjusted evaluation order when constructing
Itervia array spread.The array spread syntax
[ a, ..it, b ]previously supported constructingItertypes, but the previous semantics had unnatural evaluation order. Specifically, side effects inside the iterator corresponding toitwere computed immediately during literal construction, rather than lazily. This made the array spread syntax unable to combine potentially infinite iterators.Now, taking
([a, ..it, b] : Iter[_])as an example, the evaluation order for array spread constructing iterators is:-
Expressions
a,it, andbare immediately evaluated left-to-right when constructing the iterator -
Side effects inside iterator
itare only triggered when the corresponding element is traversed
-
-
Custom struct constructors have been adjusted and simplified.
The new syntax is as follows:
struct Point { x : Int y : Int // Nothing else needs to be written inside the struct } fn Point::Point(x : Int, y : Int) -> Point { { x, y } } test { let _ = Point(1, 2) }Compared to the old syntax, the new syntax no longer requires writing the constructor signature twice, and no longer requires defining a
newmethod to implement the constructor β it's simpler both syntactically and semantically. For library authors who want to provide a constructor while preservingnew, they can annotate#alias(new)on thefn Type::Typedeclaration, or use#alias(new, deprecated)/#alias(new, deprecated="msg")to deprecate thenewmethod to support downstream migration.The old custom constructor syntax is currently retained, but the compiler will issue deprecation warnings for it. The old syntax will be removed in the near future.
-
Added extensible enums.
Extensible enums are primarily used to allow other packages to extend new constructors for enums in the current package. For example, here in the
basepackage, theextenumkeyword is used to declare theLogEventtype, indicating that it can be extended:// base package pub(all) extenum LogEvent[T] { Info(T) Warning(T) }In the
pluginpackage, theextenumand+=keywords can be used to extend theLogEventtype from thebasepackage β here adding a newDebugconstructor:// plugin package β extends a type from another package! pub(all) extenum @base.LogEvent[T] += { Debug(T) }In the
apppackage, pattern matching on the@base.LogEventtype can match constructors from both thebasepackage and thepluginpackage. When matching constructors, the@pkg.Csyntax is required to distinguish constructors from different packages. Since theLogEventtype is extensible, a wildcard must be used to ensure exhaustiveness:// app package β both variants share one type fn[T : Show] use_event(event : @base.LogEvent[T]) -> String { match event { @base.Info(msg) => "info: \{msg}" @base.Warning(msg) => "warn: \{msg}" @plugin.Debug(msg) => "debug: \{msg}" _ => "unknown" } } -
Added
E::@pkg.Csyntax.This syntax is primarily used to support the extensible enums mentioned above. Since regular enums guarantee that the type and constructors come from the same package, the above syntax is equivalent to
@pkg.E::C. However, considering extensible enums, types and constructors may come from different packages, so explicitly indicating which package a constructor comes from becomes necessary.After supporting this syntax, constructor syntax can be summarized into the following four cases:
-
Cβ Constructor declared in the current package -
@pkg.Cβ Constructor declared in packagepkg -
@pkg.E::Cβ IfEis a regular enum, bothEandCcome from@pkg; if it's an extensible enum,Ecomes frompkgwhileCcomes from the current package -
@pkg1.E::@pkg2.CβEandCcome from@pkg1and@pkg2respectively
-
-
Reverse pipe syntax
<|now supports method calls:obj.method(args) <| last_arg obj.method() <| last_arg obj.method(args) <| (x) => { x.do_something() }Note that when there's only the
final_argparameter, the left side of<|must still be written asobj.method(), and cannot be written directly asobj.method <| final_arg. -
The update part of
forloops can now use variables introduced by the condition part. For example:for sum = 0; queue.next() is Some(elem); sum = sum + elem { // ^^^^^^ elem can be used here now } nobreak { sum } -
The following long-deprecated old syntax has been removed:
-
Old
newtypesyntaxtype T UnderlyingType; the new syntax isstruct T(UnderlyingType) -
Multiple consecutive local
fndeclarations can no longer be mutually recursive. Mutually recursive local functions need to useletrec f = .. and g = ..
-
Toolchain Updatesβ
-
Workspace now uses preferred target.
For commands that apply globally to the workspace, such as
moon build,moon check,moon test, each workspace member will be operated on using its declaredpreferred-target. This way, mixed projects, such as integrated frontend-backend projects, can complete checking, building, testing, etc., with a single command. -
Added
moon run -cfunctionality.moon run -c <script>allows direct execution of temporary scripts, for example:$ moon run -c 'fn main { println("hello") }' hello -
moon runnow resolves projects starting from a selected path.Typically, we use a local project as a tool. In such cases, the source code path differs from the working path. Previously,
moon runhad to be executed inside the project, or required an additional--manifest-path. This restriction has now been lifted to simplify usage. -
Deprecated
--manifest-path.As mentioned above,
--manifest-pathwas used in cases where the source code path differs from the working path, typically only appearing inmoon runscenarios. Since the restriction onmoon runhas been lifted, this parameter is no longer necessary. -
Experimental
moon.modconfiguration.moon.modis an experimental module configuration file used to replacemoon.mod.json, providing a writing experience consistent withmoon.pkg. The build system currently supportsmoon.mod, while LSP support is still being adapted.// Module name name = "moonbit-community/mod" // Version version = "0.1.0" // Module dependencies import { "moonbitlang/async@0.19.0", "moonbitlang/x@0.4.43", } // Other original configuration options( readme: "README.mbt.md", repository: "", license: "Apache-2.0", keywords: [ "keyword1", "keyword2" ], ... description: "", )The new
moon.moddeprecates local dependency configuration, recommendingmoon.workas a replacement.Projects using the old configuration are unaffected, and currently no migration is performed by default. You can set the environment variable
NEW_MOON_MOD=1to letmoonautomatically migrate oldmoon.mod.jsonconfigurations to the newmoon.mod. -
Build system
rule/dev_buildconfiguration.The build system has deprecated the previous
options("pre-build": ...)configuration inmoon.pkg. The improved configuration is as follows:// Define rule1 rule(name: "rule1", command: "exe $input -o $output") // Use rule1, setting the input and output to use dev_build(rule: "rule1", input: "input.txt", output: "output.mbt")Rules defined in
moon.pkgare only visible within the same configuration. Rules can also be added to the newmoon.mod, in which case the rule can be used by allmoon.pkgfiles in the entire project, reducing configuration duplication. -
Added native LSP.
This is the MoonBit LSP we've reimplemented in OCaml, compiled directly to binary, planned to replace the current TS-implemented LSP in the future. Use it via
moon lsp. VSCode plugin users can use it by setting"moonbit.nativeLsp": true. We welcome everyone to try it and provide feedback. -
Added
MOON_WORKenvironment variable to specify the location of themoon.workfile, or useMOON_WORK=offto disable workspace behavior.
Standard Library Updatesβ
-
Deprecation and migration of
Show-related implementations.We are migrating debug interfaces for most container types from
ShowtoDebug.Debugaims to provide a better debugging experience: it generates structured, indented, human-readable information for data structures. TheShowtrait will focus on generating strings in specific formats, such as having theJsontype directly output JSON-formatted text. Distinguishing betweenShowandDebugalso avoids accidentally using unprocessed data in string interpolation.This update:
-
Deprecates
Showimplementations for standard library container types, including tuples,Array,Map,Set,Option,Result -
Changes the behavior of
Show::outputregarding theStringandChartypes
Show::outputpreviously behaved differently fromShow::to_stringwhen handlingStringandChar: it would process the contents of strings and characters, outputting text with quotes and escape sequences. After the change,Show::outputis consistent withShow::to_string, and bothStringandCharare output as-is. For example:// Old behavior assert_eq(Show::output("\n"), "\"\\n\"") assert_eq(Show::output('\n'), "'\\n'") assert_eq(Show::to_string("\n"), "\n") assert_eq(Show::to_string('\n'), "\n") // Current behavior assert_eq(Show::output("\n"), "\n") assert_eq(Show::output('\n'), "\n") assert_eq(Show::to_string("\n"), "\n") assert_eq(Show::to_string('\n'), "\n")When migrating, use
Debugfor test snapshots, test assertions, and log-like output.Some common cases can be migrated as follows:
-
For custom types, use
derive(Debug)to generate the implementation. Only manually implementShowwhen the type has a meaningful specific text representation, such as theJsontype,Htmltype, orSqlStatementtype. -
Use
debug_inspect(value, content=...)instead ofinspect(value, content=...) -
Use
@debug.assert_eq(a, b)instead ofassert_eq(a, b) -
In interpolation,
"\{x}"will insert the result fromShow;"\{to_repr(x)}"will insert the result fromDebug. In most debugging scenarios, you need to useto_repr. -
Use
@debug.to_string(value)instead ofvalue.to_string(), provided the string is only used for debugging.
-
-
moonbitlang/asyncis now at version v0.19.0. The main new features and API changes since the last monthly report (v0.17.0) are as follows:-
Added the
moonbitlang/core/gzippackage, which can apply gzip decompression/compression transformations to any reader/writer -
The
moonbitlang/async/tlspackage added the following features:-
The
get_peer_certificatemethod allows users to obtain the server certificate in DER format on the client side -
The
unique_channel_bindingandserver_endpoint_channel_bindingmethods can be used to obtain the two types of TLS channel binding data defined in RFC 5929 -
The
verifyparameter of@tls.Tls::clienthas been deprecated, replaced by thetrust? : TrustedRootparameter.TrustedRoot::SystemRootandTrustedRoot::NoVerificationcorrespond to the originalverify=trueandverify=false.TrustedRoot::CustomPemFile(filename)is a new feature that allows using a custom PEM certificate file as a trusted root certificate, enabling TLS verification with private self-signed certificates -
@http.Client(..)now also accepts thetrustparameter, which is forwarded to@tls.Tls::client(..)during HTTPS communication
-
-
Added the
moonbitlang/async/raw_fdpackage, which integrates arbitrary file descriptors into themoonbitlang/asyncevent loop, making it convenient for users to operate special file descriptors not directly supported bymoonbitlang/async -
Added signal support. Now,
async fn mainprograms will automatically cancel the entire program (viamoonbitlang/async's built-in cancellation mechanism) when receiving signals like Ctrl+C, facilitating process-level cleanup. By default, the following signals trigger global cancellation:-
Linux/MacOS: SIGINT, SIGTERM, SIGHUP
-
Windows: CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT
-
-
You can control which signals trigger global cancellation behavior via the
set_global_cancellation_signalsfunction in themoonbitlang/async/signalpackage. -
Added UDP multicast support. See https://github.com/moonbitlang/async/pull/354 for details
-
@socket.Addr::parsenow correctly handles IPv6 zone suffixes -
moonbitlang/async/httpchanged how cookies are handled. Receiving and sendingSet-Cookieheaders now needs to be done via the@http.Response.cookiesfield rather than directly throughheaders. This is because theSet-Cookieheader may appear multiple times in the same response and doesn't follow some RFC rules, making it impossible to fit into aMap.moonbitlang/async/httpperforms basic parsing ofSet-Cookiecontents for easier user reading -
When users don't explicitly provide
Accept-Encoding,@http.Clientnow automatically requests gzip compression from the server and automatically decompresses when users read the response body. If users explicitly provideAccept-Encoding, the response body sent by the target server is read as-is -
Content-Lengthcan now be explicitly provided when sending HTTP requests/responses. In this case, users can still incrementally send data in batches, andmoonbitlang/async/httpwill automatically verify whether the total data length sent by the user is correct, reporting an error if incorrect. This feature helps applications like file download servers provide download progress support to clients -
APIs like
@fs.opennow usecreate_mode? : CreateModeto control whether to create a new file and whether to truncate existing files, andpermission? : Intto control access permissions for newly created files (default0o644). The oldcreateandtruncateparameters are deprecated. The permission parameter of@fs.mkdiris now optional, defaulting to0o755 -
Added
@fs.rename, which can be used to asynchronously perform file rename operations -
Added
@fs.Directory::next, which returns an@fs.DirectoryEntrystruct containing the filename and additional information such as whether the file is a subdirectory -
@fs.File::as_diris no longer deprecated and can be used normally going forward
-