Introducing virtual package in MoonBit
MoonBit recently introduced a new feature: virtual package. By declaring a package as a virtual package and defining a set of interfaces (via .mbti
files), users can choose which implementation to use. If no implementation is specified, the default implementation of the virtual package is used. This feature provides significant flexibility in separating interfaces from implementations. Note: This feature is currently in an experimental state.
Usageβ
Taking the virtual_pkg_demo as an example, the project structure is as follows:
.
βββ virtual // Declared as a virtual package, with virtual.mbti defining a set of interfaces
βββ impl // Provides a custom implementation for the interfaces in the virtual package
βββ lib // Depends on the interfaces in the virtual package
βββ main // Depends on the lib package, overrides the virtual package's default implementation with the impl package
-
Declaring a Virtual Package: In the
moon.pkg.json
file of thevirtual
package, use the following field to declare it as a virtual package. The package must include a.mbti
file, or an error will be reported. If"has-default"
is set totrue
, the compiled artifacts of the default implementation will be built and linked when the package is not overridden."virtual": {
"has-default": true
} -
Providing a Custom Implementation for a Virtual Package: In the
moon.pkg.json
file of theimpl
package, set the"implement"
field to the full package name of the virtual package. The implementation must fully satisfy the interfaces defined in the.mbti
file of the virtual package."implement": "username/hello/virtual"
-
Using a Virtual Package: In the
moon.pkg.json
file of thelib
package, add the virtual package in the"import"
field. -
Overriding a Virtual Package: In the
moon.pkg.json
file of themain
package, specify the implementation package in the"overrides"
field. If no implementation is specified and the virtual package has a default implementation, the default is used. If there is no default implementation, an error is reported.{
"is-main": true,
"import": [
"username/hello/lib"
],
"overrides": [
"username/hello/impl"
]
}
Specific Exampleβ
The following example illustrates the use of virtual packages in moonbitlang/core
. The moonbitlang/core/abort package
is defined as a virtual package. The relevant code is as follows:
-
abort.mbti: Declares the APIs exposed by this package.
package "moonbitlang/core/abort"
// Values
fn abort[T](String) -> T
// Types and methods
// Type aliases
// Traits -
abort.mbt: Provides the default implementation for the
fn abort[T](String) -> T
API.pub fn abort[T](msg : String) -> T {
let _ = msg
panic_impl()
}
///|
fn panic_impl[T]() -> T = "%panic"
In the current default implementation, the msg
parameter is intentionally ignored. This is because using println(msg)
would make the generated .wasm
file depend on the spectest::print_char
function defined in the moonrun
runtime. If a WebAssembly runtime other than moonrun
(e.g., Wasmtime, Wasmer) is used, it would result in an error:
error: Unable to instantiate the WebAssembly module
β°ββΆ 1: Error while importing "spectest"."print_char": unknown import. Expected Function(FunctionType { params: [I32], results: [] })
Customizing the Implementation of moonbitlang/core/abortβ
The abort(msg)
interface is used in many built-in data structures. When certain invariants are not met, it causes a runtime failure. However, with the current default implementation, the msg
in abort
is not output, making it difficult for users to identify the cause of the error. To address this, we can provide a custom implementation of abort
that prints the msg
(provided you are not using a WebAssembly runtime other than moonrun
). The steps are as follows:
First, run moon add moonbitlang/dummy_abort
, then add the following field to moon.pkg.json
:
"overrides": [
// This package provides a custom implementation for the moonbitlang/core/abort virtual package
"moonbitlang/dummy_abort/abort_show_msg"
]
Testing the behavior of abort
now shows that the passed parameter is indeed printed.
Intentionally misusing swap
on an array results in the following error message:
In the future, the MoonBit community plans to provide libraries compliant with the WASI standard to implement println
. This will allow compiled .wasm
files to run on WebAssembly runtimes that support this standard. Stay tuned!
New to MoonBit?
- Download MoonBit.
- Explore MoonBit Beginner's Guide.
- Play with MoonBit Language Tour.
- Check out MoonBit Docs.
- Join our Discord community.