How to use WASM code from ClojureScript through JavaScript interop
Clojure is a great programming language. I have been working with it lately, and it’s been a blast. Another language that I love is Rust. One day I was thinking about both languages, their advantages and disadvantages, and under which circumstances I should use each. That made me wonder if it would be hard to use WASM from Clojurescript.
My idea was to:
- Create a simple function in Rust
- Use wasm-bindgen crate to generate the WASM code and JavaScript bindings.
- Load the code into JavaScript
- Call WASM code from ClojureScript through the JavaScript interops
You can check the final code at https://github.com/danielorihuela/wasm-on-clojurescript-through-js-interop.
The first two steps were easy. I created a simple Rust function.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: u8, b: u8) -> u8 {
a + b
}
Then, I built the WASM code and JavaScript bindings with the following command:
wasm-pack build --target web
Now is when the interesting part starts. We want to use the generated JavaScript bindings to call the WASM code. First, we need to create a simple website and load the code in the browser. In our case, we want to access the WASM functions from the “windows” object. So I ended up with the following piece of code.
<script type="module">
import init, { add } from './wasm/addition.js'
async function run() {
await init();
window.addWasm = add;
}
await run();
</script>
The WASM is now loaded. We are ready to execute the “hello world” website.
clj -M --main cljs.main --compile hello-world.core --repl
But there’s one problem.
The wasm file cannot be validated. I suspect it is related to Will Cohen’s comment on https://clojure.atlassian.net/browse/CLJS-3387. So I tried to serve it with miniserve. It worked!
We can call WASM code with with the following approach: The cheatsheet tells us how to access the function we jsut exported.
(. js/window (addWasm 5 10))
You can check the final code at https://github.com/danielorihuela/wasm-on-clojurescript-through-js-interop.
And this is how we can call WASM from ClojureScript from a basic project. The problem with that approach is that we lost the REPL since we cannot use the ClojureScript browser. However, you will probably use a build tool like shadow-cljs. An approach exists for shadow-cljs to use WASM with the REPL. You can check it https://github.com/thheller/wasm-pack-cljs.