Run JSaddle @JSM@ with the GHC WASM backend.
Run JSaddle JSM
with the GHC WASM backend.
jsaddle-wasm
[](https://hackage.haskell.org/package/jsaddle-wasm )
Run JSaddleJSM
actions with the GHC WASM backend.
This can for example be used to compile and run Miso or Reflex apps in the browser.
[!IMPORTANT] This project is in an early stage.
Examples
Miso
Several Miso examples: https://github.com/tweag/ghc-wasm-miso-examples
How to use
Install a WASM-enabled GHC with support for the WASM JSFFI from ghc-wasm-meta (GHC 9.10 or newer).
Assuming you built your application as an app :: JSM ()
:
import Language.Javascript.JSaddle.Wasm qualified as JSaddle.Wasm
foreign export javascript "hs_start" main :: IO ()
main :: IO ()
main = JSaddle.Wasm.run app
Build the WASM binary with the following GHC options:
ghc-options: -no-hs-main -optl-mexec-model=reactor "-optl-Wl,--export=hs_start"
Now, run the post-linker script as described in the GHC User's Guide; we will call the resulting JavaScript file ghc_wasm_jsffi.js
.
Then, following the GHC User's Guide, you can run the WASM binary in the browser via e.g. browser_wasi_shim:
import { WASI, OpenFile, File, ConsoleStdout } from "@bjorn3/browser_wasi_shim";
import ghc_wasm_jsffi from "./ghc_wasm_jsffi.js";
const fds = [
new OpenFile(new File([])), // stdin
ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)),
ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)),
];
const options = { debug: false };
const wasi = new WASI([], [], fds, options);
const instance_exports = {};
const { instance } = await WebAssembly.instantiateStreaming(fetch("app.wasm"), {
wasi_snapshot_preview1: wasi.wasiImport,
ghc_wasm_jsffi: ghc_wasm_jsffi(instance_exports),
});
Object.assign(instance_exports, instance.exports);
wasi.initialize(instance);
await instance.exports.hs_start();
Potential future work
- Take a closer look at synchronous callbacks (no special handling currently, but basic things like
stopPropagation
already seem to work fine). - Testing (e.g. via Selenium).
- Add logging/stats.
- Performance/benchmarking (not clear that this is actually a bottleneck for most applications).
Optimize existing command-based implementation.
- Reuse buffers
- Use a serialization format more efficient than JSON.
Implement
ghcjs-dom
API directly via the WASM JS FFI.This would involve creating a
ghcjs-dom-wasm
package by adapting the FFI import syntax fromghcjs-dom-jsffi
/ghcjs-dom-javascript
appropriately.Currently, the generic
ghcjs-dom-jsaddle
seems to work fine, so it seems sensible to wait with this until benchmarks or other concerns motivate this.
Related projects
- WebGHC/jsaddle-wasm for the analogue for WebGHC instead of the GHC WASM backend.