diff --git a/component-model/examples/tutorial/moonbit/adder-with-error/stub.mbt b/component-model/examples/tutorial/moonbit/adder-with-error/stub.mbt new file mode 100644 index 00000000..00ebf417 --- /dev/null +++ b/component-model/examples/tutorial/moonbit/adder-with-error/stub.mbt @@ -0,0 +1,7 @@ +// Generated by `wit-bindgen` 0.45.0. + +///| +fn init { + let _ = @add.add(1, 2).unwrap_or_error() catch { Overflow => ... } + +} diff --git a/component-model/examples/tutorial/moonbit/adder-with-error/top.mbt b/component-model/examples/tutorial/moonbit/adder-with-error/top.mbt new file mode 100644 index 00000000..f5268bcf --- /dev/null +++ b/component-model/examples/tutorial/moonbit/adder-with-error/top.mbt @@ -0,0 +1,25 @@ +// Generated by `wit-bindgen` 0.45.0. DO NOT EDIT! + +///| +pub(all) suberror ComputationError { + Overflow +} derive(Show, Eq) + +///| +pub fn add(x : UInt, y : UInt) -> Result[UInt, ComputationError] { + let return_area = @ffi.malloc(8) + wasmImportAdd(x.reinterpret_as_int(), y.reinterpret_as_int(), return_area) + let lifted4 = match @ffi.load8_u(return_area + 0) { + 0 => Result::Ok(@ffi.load32(return_area + 4).reinterpret_as_uint()) + 1 => { + let lifted = match @ffi.load8_u(return_area + 4) { + 0 => ComputationError::Overflow + _ => panic() + } + Result::Err(lifted) + } + _ => panic() + } + @ffi.free(return_area) + return lifted4 +} diff --git a/component-model/examples/tutorial/moonbit/adder-with-error/world.wit b/component-model/examples/tutorial/moonbit/adder-with-error/world.wit new file mode 100644 index 00000000..b41acc96 --- /dev/null +++ b/component-model/examples/tutorial/moonbit/adder-with-error/world.wit @@ -0,0 +1,12 @@ +package docs:adder@0.1.0; + +interface add { + variant computation-error { + overflow + } + add: func(x: u32, y: u32) -> result; +} + +world adder { + import add; +} diff --git a/component-model/examples/tutorial/moonbit/adder/moon.pkg.json b/component-model/examples/tutorial/moonbit/adder/moon.pkg.json new file mode 100644 index 00000000..f811c802 --- /dev/null +++ b/component-model/examples/tutorial/moonbit/adder/moon.pkg.json @@ -0,0 +1,25 @@ +{ + // link configuration for Wasm backend + "link": { + "wasm": { + "exports": [ + // Export for cabi_realloc + "cabi_realloc:cabi_realloc", + // Export per the interface definition + "wasmExportAdd:docs:adder/add@0.1.0#add" + ], + "export-memory-name": "memory", + "heap-start-address": 16 + } + }, + "import": [ + { + "path": "docs/adder/ffi", + "alias": "ffi" + }, + { + "path": "docs/adder/gen/interface/docs/adder/add", + "alias": "add" + } + ] +} \ No newline at end of file diff --git a/component-model/examples/tutorial/moonbit/adder/stub.mbt b/component-model/examples/tutorial/moonbit/adder/stub.mbt new file mode 100644 index 00000000..c57a3e24 --- /dev/null +++ b/component-model/examples/tutorial/moonbit/adder/stub.mbt @@ -0,0 +1,6 @@ +// Generated by `wit-bindgen` 0.45.0. + +///| +pub fn add(x : UInt, y : UInt) -> UInt { + x + y +} diff --git a/component-model/src/SUMMARY.md b/component-model/src/SUMMARY.md index 3512e3dd..4dc11ece 100644 --- a/component-model/src/SUMMARY.md +++ b/component-model/src/SUMMARY.md @@ -23,6 +23,7 @@ - [JavaScript](./language-support/javascript.md) - [Python](./language-support/python.md) - [Rust](./language-support/rust.md) + - [MoonBit](./language-support/moonbit.md) - [WebAssembly Text Format (WAT)](./language-support/wat.md) - [Other Languages](./language-support/other-languages.md) - [Running Components](./running-components.md) diff --git a/component-model/src/introduction.md b/component-model/src/introduction.md index 1ed749b9..992e4019 100644 --- a/component-model/src/introduction.md +++ b/component-model/src/introduction.md @@ -19,6 +19,7 @@ This documentation is aimed at _users_ of the component model: developers of lib | [Worlds] | [JavaScript] | | | | [Python] | | | | [Rust] | | +| | [MoonBit] | | [Why Components?]: ./design/why-component-model.md [Components]: ./design/components.md @@ -31,6 +32,7 @@ This documentation is aimed at _users_ of the component model: developers of lib [JavaScript]: ./language-support/javascript.md [Python]: ./language-support/python.md [Rust]: ./language-support/rust.md +[MoonBit]: ./language-support/moonbit.md [Composing]: ./composing-and-distributing/composing.md [Running]: ./running-components.md diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 07f60781..67e52941 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -37,6 +37,7 @@ without using a higher-level language front-end. - [Rust Tooling](./language-support/rust.md) - [Building a Component with `cargo component`](./language-support/rust.md#building-a-component-with-cargo-component) - [Running a Component from Rust Applications](./language-support/rust.md#running-a-component-from-rust-appliacations) + - [MoonBit Tooling](./language-support/moonbit.md) - [WebAssembly Text Format (WAT)](./language-support/wat.md#wat-webassembly-text-format) - [Building a Component from WAT with `wasm-tools`](./language-support/wat.md#building-a-component-with-wasm-tools) - [Running a Component with Wasmtime](./language-support/wat.md#running-a-component-with-wasmtime) diff --git a/component-model/src/language-support/moonbit.md b/component-model/src/language-support/moonbit.md new file mode 100644 index 00000000..80481558 --- /dev/null +++ b/component-model/src/language-support/moonbit.md @@ -0,0 +1,293 @@ +# MoonBit + +[MoonBit](https://www.moonbitlang.com/) is a programming language that provides first-class support for modern WebAssembly, including WebAssembly components. + +This guide demonstrates how to build WebAssembly components using +MoonBit, leveraging WIT (WebAssembly Interface Types) for interface definitions +and the `wit-bindgen` toolchain for code generation. + +This tutorial walks through building a component that implements the +[`adder` world][adder-wit] defined in the `docs:adder` package. The component +will export an `add` interface containing an `add` function that sums two +numbers. + +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit + +## 1. Install the Tools + +### Installing MoonBit + +First, install the MoonBit compiler and toolchain. Follow the installation +instructions from the +[MoonBit download page](https://www.moonbitlang.com/download). + +Verify your MoonBit installation (below are the versions at the time of +writing): + +```console +$ moon version --all +moon 0.1.20250826 (8ab6c9e 2025-08-26) ~/.moon/bin/moon +moonc v0.6.25+d6913262c (2025-08-27) ~/.moon/bin/moonc +moonrun 0.1.20250826 (8ab6c9e 2025-08-26) ~/.moon/bin/moonrun +moon-pilot 0.0.1-95f12db ~/.moon/bin/moon-pilot +``` + +### Installing Wasm toolchains + +You need to have the following tools: + +- [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) for generating MoonBit bindings from WIT +- [wasm-tools](https://github.com/bytecodealliance/wasm-tools) for component conversion +- (optional) [wasmtime](https://github.com/bytecodealliance/wasmtime) for Wasm runtime + +You may choose to download the executable from the respective GitHub releases, or you may, +with [Rust toolchain](https://www.rust-lang.org/tools/install) installed: + +```sh +cargo install wit-bindgen-cli +cargo install wasm-tools +cargo install wasmtime-cli +``` + +Verify the installations (below are the versions at the time of writing): + +```console +$ wit-bindgen --version +wit-bindgen-cli 0.45.0 +$ wasm-tools --version +wasm-tools 1.238.0 +$ wasmtime --version +wasmtime 36.0.2 +``` + +## 2. Define the Interface (WIT) + +Before generating the MoonBit project, you need to define the component +interface using WIT. + +Create a directory for your project and define the WIT +file: + +```sh +mkdir moonbit-adder && cd moonbit-adder +mkdir wit +``` + +Create `wit/world.wit` with the following content: + +```wit +{{#include ../../examples/tutorial/wit/adder/world.wit}} +``` + +This WIT definition: + +- Declares a package `docs:adder` with version `0.1.0` +- Defines an `add` interface with a single function that takes two `u32` + parameters and returns a `u32` +- Creates an `adder` world that exports the `add` interface + +## 3. Generate MoonBit Project Structure + +Use `wit-bindgen` to generate the MoonBit project structure and bindings: + +```sh +wit-bindgen moonbit wit/world.wit --out-dir . \ + --derive-eq \ + --derive-show \ + --derive-error +``` + +This command generates the following directory structure: + +``` +. +├── ffi +│ ├── moon.pkg.json +│ └── top.mbt +├── gen +│ ├── ffi.mbt +│ ├── gen_interface_docs_adder_add_export.mbt +│ ├── interface +│ │ └── docs +│ │ └── adder +│ │ └── add +│ │ ├── moon.pkg.json +│ │ ├── stub.mbt +│ │ └── top.mbt +│ ├── moon.pkg.json +│ ├── world +│ │ └── adder +│ │ ├── moon.pkg.json +│ │ └── stub.mbt +│ └── world_adder_export.mbt +├── moon.mod.json +├── wit +│ └── world.wit +└── world + └── adder + ├── ffi_import.mbt + ├── import.mbt + ├── moon.pkg.json + └── top.mbt +``` + +The generated files include: + +- `moon.mod.json`: MoonBit module configuration +- `gen/`: Generated export bindings + - `interface/`: Generated export interface bindings + - `world/`: Generated export world bindings + - `stub.mbt`: Main implementation file +- `interface/`: Generated import interface bindings +- `world/`: Generated import world bindings + +The `wit-bindgen` tool generates MoonBit bindings that handle the WebAssembly +component interface. + +If you execute `moon check`, there will be a warning +suggesting that file `gen/interface/docs/adder/add/stub.mbt` contains unfinished +code, which is what we need to complete. + +## 4. Implement the Component Logic + +Implement the `add` function in `gen/interface/docs/adder/add/stub.mbt`: + +```rust +{{#include ../../examples/tutorial/moonbit/adder/stub.mbt}} +``` + +## 5. Configure the Build + +Ensure your `gen/moon.pkg.json` is properly configured for WebAssembly target: + +```json +{{#include ../../examples/tutorial/moonbit/adder/moon.pkg.json}} +``` + +## 5. Build the WebAssembly Component + +Build the MoonBit code to WebAssembly core module: + +```sh +moon build +``` + +To create a proper WebAssembly component from the module we have produced, use `wasm-tools`: + +```sh +wasm-tools component embed wit target/wasm/release/build/gen/gen.wasm \ + --encoding utf16 \ + --output adder.wasm +wasm-tools component new adder.wasm --output adder.component.wasm +``` + +You can verify the component's interface using `wasm-tools`: + +```sh +wasm-tools component wit adder.component.wasm +``` + +The WIT printed should be similar if not exactly the same as the following: + +```wit +package root:component; + +world root { + export docs:adder/add@0.1.0; +} +package docs:adder@0.1.0 { + interface add { + add: func(x: u32, y: u32) -> u32; + } +} +``` + +## 6. Testing the Component + +### Using the Example Host + +To test your component, use the [`example-host`][example-host] provided in this +repository: + +```sh +git clone https://github.com/bytecodealliance/component-docs.git +cd component-docs/component-model/examples/example-host +cp /path/to/adder.component.wasm . +cargo run --release -- 5 3 adder.component.wasm +``` + +Expected output: + +``` +5 + 3 = 8 +``` + +[example-host]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/README.md + +### Using Wasmtime + +You can also test the component directly with `wasmtime`: + +```console +$ wasmtime run --invoke 'add(10, 20)' adder.component.wasm +30 +``` + +## Advanced + +### --derive-eq --derive-show + +These two options will add `derive(Eq)` and / or `derive(Show)` for all the +generated types. + +### --derive-error + +This option will generate variants / enums whose names containing 'Error' as +[suberrors](https://docs.moonbitlang.com/en/latest/language/error-handling.html#error-types). +This allows you to integrate the MoonBit's error handling easier. + +For example, for the following interface: + +```wit +{{#include ../../examples/tutorial/moonbit/adder-with-error/world.wit}} +``` + +Will generate the following type and function + +```rust +{{#include ../../examples/tutorial/moonbit/adder-with-error/top.mbt}} +``` + +which you may use it as: + +```rust +{{#include ../../examples/tutorial/moonbit/adder-with-error/stub.mbt}} +``` + +### --ignore-stub + +It happens when you would like to regenerate the project due to the updated +interface, but you don't want the `stub` file to be touched. You may use +`--ignore-stub` option to avoid such modifications. + +### --project-name + +By default, the project name is generated per the name defined in the MoonBit +file. You may use this option to specify the name of the project. It can also be +used if you are generating the project as part of a larger project. + +### --gen-dir + +By default, the exportation parts are generated under `gen`. You may use this +option to specify another directory. + +## References and Further Reading + +- [MoonBit Official Website](https://www.moonbitlang.com/) +- [MoonBit Language Documentation](https://docs.moonbitlang.com/) +- [WebAssembly Component Model](https://component-model.bytecodealliance.org/) +- [WIT Format Specification](https://component-model.bytecodealliance.org/design/wit.html) +- [`wit-bindgen` Documentation](https://github.com/bytecodealliance/wit-bindgen) +- [WebAssembly Tools](https://github.com/bytecodealliance/wasm-tools) +- [Wasmtime Runtime](https://wasmtime.dev/) +- [Component Model Examples](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples)