jsaddle-wasm: Run JSaddle JSM with the GHC WASM backend

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Run JSaddle JSM with the GHC WASM backend.


[Skip to Readme]

Properties

Versions 0.0.0.0, 0.0.1.0, 0.0.1.0
Change log CHANGELOG.md
Dependencies aeson (>=2 && <2.3), base (>=4.16 && <5), bytestring (>=0.11 && <0.13), ghc-experimental (>=0.1 && <0.2), jsaddle (>=0.9 && <0.10), jsaddle-wasm, stm (>=2.5 && <2.6) [details]
License CC0-1.0
Author amesgen
Maintainer amesgen@amesgen.de
Category Web, Javascript
Home page https://github.com/amesgen/jsaddle-wasm
Bug tracker https://github.com/amesgen/jsaddle-wasm/issues
Source repo head: git clone https://github.com/amesgen/jsaddle-wasm
Uploaded by amesgen at 2024-10-26T13:15:21Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for jsaddle-wasm-0.0.1.0

[back to package description]

jsaddle-wasm

CI Hackage Haddocks

Run JSaddle JSM 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

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();

Separating execution environments

It is also possible to run the WASM worker in a different execution environment (e.g. a web worker) than the JSaddle JavaScript code that dispatches the JSaddle command messages.

An advantage of this approach is that computationally expensive operations in WASM do not block the UI thread. A disadvantage is that there is some overhead for copying the data back and forth, and everything relying on synchronous callbacks (e.g. stopPropagation/preventDefault) definitely no longer works.

Potential future work