Skip to content

Commit

Permalink
Chores (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
amesgen authored Dec 10, 2024
1 parent 9d58342 commit 6ede9df
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 34 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Hackage](https://img.shields.io/hackage/v/jsaddle-wasm)](https://hackage.haskell.org/package/jsaddle-wasm)
[![Haddocks](https://img.shields.io/badge/documentation-Haddocks-purple)](https://hackage.haskell.org/package/jsaddle-wasm/docs/Language-Javascript-JSaddle-Wasm.html)

Run [JSaddle][] `JSM` actions with the [GHC WASM backend][].
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.

Expand All @@ -21,7 +21,7 @@ This can for example be used to compile and run [Miso][] or [Reflex][] apps in t

## How to use

Install a WASM-enabled GHC with support for the WASM JSFFI from [ghc-wasm-meta][] (GHC 9.10 or newer).
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 ()`:

Expand All @@ -34,14 +34,14 @@ main :: IO ()
main = JSaddle.Wasm.run app
```

Build the WASM binary with the following GHC options:
Build the Wasm binary with the following GHC options:
```cabal
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][ghc-users-guide-js-api]; we will call the resulting JavaScript file `ghc_wasm_jsffi.js`.

Then, following the [GHC User's Guide][ghc-users-guide-js-api], you can run the WASM binary in the browser via e.g. [browser_wasi_shim][]:
Then, following the [GHC User's Guide][ghc-users-guide-js-api], you can run the Wasm binary in the browser via e.g. [browser_wasi_shim][]:
```javascript
import { WASI, OpenFile, File, ConsoleStdout } from "@bjorn3/browser_wasi_shim";
import ghc_wasm_jsffi from "./ghc_wasm_jsffi.js";
Expand All @@ -67,24 +67,24 @@ 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.
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.
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.

- Instead of the `run` function above, you need to use `runWorker`:
- Instead of the `run` function above, you need to use `runWorker` (again assuming `app :: JSM ()`):

```haskell
import Language.Javascript.JSaddle.Wasm qualified as JSaddle.Wasm

foreign export javascript "hs_runWorker" runWorker :: JSVal -> IO ()

runWorker :: JSVal -> IO ()
runWorker = JSaddle.Wasm.runWorker Ormolu.Live.app
runWorker = JSaddle.Wasm.runWorker app
```

The argument to `runWorker` here can be any message port in the sense of the [Channel Messaging API][]. In particular, it must provide a `postMessage` function and a `message` event.

For example, in a web worker, you can initialize the WASM module as above, and then run
For example, in a web worker, you can initialize the Wasm module as above, and then run
```javascript
await instance.exports.hs_runWorker(globalThis);
```
Expand All @@ -111,22 +111,22 @@ An advantage of this approach is that computationally expensive operations in WA
- Optimize existing command-based implementation.
- Reuse buffers
- Use a serialization format more efficient than JSON.
- Patch `jsaddle` to not go through commands, by using the WASM JS FFI.
- Implement `ghcjs-dom` API directly via the WASM JS FFI.
- Patch `jsaddle` to not go through commands, by using the Wasm JS FFI.
- 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 from `ghcjs-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](https://github.com/WebGHC/jsaddle-wasm) for the analogue for [WebGHC][] instead of the [GHC WASM backend][].
- [WebGHC/jsaddle-wasm](https://github.com/WebGHC/jsaddle-wasm) for the analogue for [WebGHC][] instead of the [GHC Wasm backend][].

[JSaddle]: https://github.com/ghcjs/jsaddle
[GHC WASM backend]: https://www.tweag.io/blog/2022-11-22-wasm-backend-merged-in-ghc
[GHC Wasm backend]: https://www.tweag.io/blog/2022-11-22-wasm-backend-merged-in-ghc
[Miso]: https://github.com/dmjio/miso
[Reflex]: https://github.com/reflex-frp/reflex
[ghc-wasm-meta]: https://gitlab.haskell.org/ghc/ghc-wasm-meta
[ghc-wasm-meta]: https://gitlab.haskell.org/haskell-wasm/ghc-wasm-meta
[browser_wasi_shim]: https://github.com/bjorn3/browser_wasi_shim
[ghc-users-guide-js-api]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/wasm.html#the-javascript-api
[WebGHC]: https://webghc.github.io
Expand Down
18 changes: 9 additions & 9 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
inputs = {
nixpkgs.follows = "ghc-wasm-meta/nixpkgs";
flake-utils.follows = "ghc-wasm-meta/flake-utils";
ghc-wasm-meta.url = "gitlab:ghc/ghc-wasm-meta?host=gitlab.haskell.org";
ghc-wasm-meta.url = "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org";
};
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
let pkgs = inputs.nixpkgs.legacyPackages.${system};
Expand Down
4 changes: 2 additions & 2 deletions jsaddle-wasm.cabal
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
cabal-version: 3.0
name: jsaddle-wasm
version: 0.0.1.0
synopsis: Run JSaddle JSM with the GHC WASM backend
description: Run JSaddle @JSM@ with the GHC WASM backend.
synopsis: Run JSaddle JSM with the GHC Wasm backend
description: Run JSaddle @JSM@ with the GHC Wasm backend.
category: Web, Javascript
homepage: https://github.com/amesgen/jsaddle-wasm
bug-reports: https://github.com/amesgen/jsaddle-wasm/issues
Expand Down
4 changes: 2 additions & 2 deletions src-native/Language/Javascript/JSaddle/Wasm/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import Language.Javascript.JSaddle.Types (JSM)

run :: JSM () -> IO ()
run _ =
fail "Language.Javascript.JSaddle.Wasm.run: only works on WASM backend"
fail "Language.Javascript.JSaddle.Wasm.run: only works on Wasm backend"

runWorker :: JSM () -> JSVal -> IO ()
runWorker _ _ =
fail "Language.Javascript.JSaddle.Wasm.runWorker: only works on WASM backend"
fail "Language.Javascript.JSaddle.Wasm.runWorker: only works on Wasm backend"

data JSVal
6 changes: 3 additions & 3 deletions src-wasm/Language/Javascript/JSaddle/Wasm/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import Language.Javascript.JSaddle.Run.Files qualified as JSaddle.Files
import Language.Javascript.JSaddle.Types (Batch, JSM)

-- Note: It is also possible to implement this succinctly on top of 'runWorker'
-- and 'jsaddleScript', but then e.g. @stopPropagation@/@preventDefault@
-- definitely don't work, whereas they work (at least in simple cases) with the
-- implementation below.
-- and 'jsaddleScript' (using MessageChannel), but then e.g.
-- @stopPropagation@/@preventDefault@ definitely don't work, whereas they work
-- (at least in simple cases) with the implementation below.
run :: JSM () -> IO ()
run entryPoint = do
-- TODO rather use a bounded (even size 1) queue?
Expand Down
6 changes: 3 additions & 3 deletions src/Language/Javascript/JSaddle/Wasm.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- | See the [README](https://github.com/amesgen/jsaddle-wasm) for more details.
--
-- While this package also compiles on non-WASM GHCs for convenience, running
-- any function from this module will immediately fail.
-- While this package also compiles on non-Wasm GHCs for convenience, running
-- any function from this module will immediately fail in that case.
module Language.Javascript.JSaddle.Wasm
( run,
runWorker,
Expand All @@ -17,7 +17,7 @@ import Language.Javascript.JSaddle.Wasm.Internal (JSVal)
import Language.Javascript.JSaddle.Wasm.Internal qualified as Internal
import Language.Javascript.JSaddle.Wasm.JS (jsaddleScript)

-- | Run a 'JSM' action via the WASM JavaScript FFI.
-- | Run a 'JSM' action via the Wasm JavaScript FFI.
run :: JSM () -> IO ()
run = Internal.run

Expand Down

0 comments on commit 6ede9df

Please sign in to comment.