Skip to main content

Bringing Scala to Server-Side Wasm with WASI & Component Model

Picture of Rikito Taniguchi, Scala Compiler (Wasm/Native)

Rikito Taniguchi

Scala Compiler (Wasm/Native)
Oct 28, 2025|18 min read
Wasm_logo
1scalaJSLinkerConfig := {
2 scalaJSLinkerConfig.value
3 .withExperimentalUseWebAssembly(true)
4 .withModuleKind(ModuleKind.ESModule)
5},
comparison_wasm_js_jvm

1import gears.async.*
2import gears.async.AsyncOperations.*
3import gears.async.default.given
4import scala.concurrent.duration.*
5
6Async.fromSync:
7 def c1 = Future { sleep(100.millisecond); 3 }
8 def c2 = Future { sleep(200.millisecond); 1 }
9 val res = Seq(c1, c2).awaitAll.sum
10 assertEquals(res, 4
1const importObj = {
2 env: { logger: (num) => console.log(num); }
3}
4WebAssembly.instantiateStreaming(fetch("main.wasm"), importObj);
1;; main.wasm
2(module
3 (import "env" "logger" (func $logger (param i32)))
4 (func $main
5 i32.const 100
6 call $logger)
7 (start $main)
8)
1(import "wasi:cli/stdin@0.2.0" "get-stdin" (func $get_stdin (result i32)))
2
3(import "wasi:io/streams@0.2.0" "[method]output-stream.blocking-write-and-flush"
4 (func $blocking_write_and_flush (param i32 i32 i32 i32)))
1$ cat hello.txt
2Hello, World!
3
4$ wasmedge foo.wasm hello.txt
5Error opening file: No such file or directory
6
7$ wasmedge --dir=.:. foo.wasm hello.txt
8Hello, World!
Image Alt

1package guest:greeter;
2interface greeter {
3 greet: func(name: string) -> string;
4}
5world greeter-world {
6 import greeter;
7}
1pub fn greet(name: &str) -> _rt::String { ... }
1@ComponentImport("guest:greeter/greeter", "greet")
2def greet(name: String): String = cm.native
1package scala-wasm:helloworld;
2
3interface greeter {
4 greet: func(name: string) -> string;
5}
6
7world scala {
8 export wasi:cli/run@0.2.0;
9 import wasi:cli/stdout@0.2.0;
10 import wasi:io/streams@0.2.0;
11 import greeter;
12}
13
14world rust {
15 export greeter;
16}
1impl Guest for Component {
2 fn greet(content: String) -> String {
3 let mut buf = Vec::new();
4 let message = format!("Hello {}!", content.as_str());
5 ferris_says::say(&message, 80, &mut buf).unwrap();
6 return String::from_utf8(buf).unwrap();
7 }
8}
1package helloworld
2
3import scalajs.component.annotation._
4import scalajs.{component => cm}
5
6object HelloWorld {
7 @ComponentExport("wasi:cli/run@0.2.0", "run")
8 def run(): cm.Result[Unit, Unit] = {
9 val greeting = greet("Scala")
10 println(greeting)
11 new cm.Ok(())
12 }
13
14 @ComponentImport("scala-wasm:helloworld/greeter", "greet")
15 def greet(name: String): String = cm.native
16}
1# componentize the Scala Wasm module
2$ wasm-tools component embed wit .2.12/target/scala-2.12/helloworld-component-model-fastopt/main.wasm -o main.wasm -w scala --encoding utf16
3$ wasm-tools component new main.wasm -o main.wasm
4# Compose Scala and Rust components
5$ wac plug --plug rust-greeter/target/wasm32-wasip1/release/rust_greeter.wasm main.wasm -o out.wasm
6$ wasmtime -W function-references,gc out.wasm
7 ______________
8< Hello Scala! >
9 --------------
10 \
11 \
12 _~^~^~_
13 \) / o o \ (/
14 '_ - _'
15 / '-----' \
1interface stdout {
2 @since(version = 0.2.0)
3 get-stdout: func() -> output-stream;
4}

Subscribe to our newsletter and never miss an article