Javascript & WebAssembly backend

Hello Haskellers,
I saw that GHC will have a javascript backend. This sounds exiting!
What is the prospect for the community?
Will the backend be available by default when downloading GHC? For the moment I have to compile it, which is of course not ideal.
Will it spur a new ecosystem of libraries for the web? Such as ReactJS bindings, Web frameworks similar to ELM…
I’m currently learning Purescript (a great language for the web based on Haskell), will it supersede it?
Purescript as a library/framework called Halogen, will something similar be implemented for Haskell?
I moved to Purescript because I wanted to create an in-browser game, which looked difficult to do in Haskell.
Cheers

2 Likes

It likely won’t be available by default, as we do not provide prebuilts for cross compilers; and this bring in the whole issue of dealing with cross toolchains. At some point we might have official ones. If you have nix, you can use our devx shell (based on haskell.nix compilers) which has prebuilt’s with

nix develop github:input-output-hk/devx#ghc962-js-minimal

Will this spur a new ecosystem? We can only hope so, no one knows yet.

Purescript is different from Haskell, and I believe both will continue to coexist. If someone will write a Halogen like library for the JS and/or WASM backends (or is in the process of doing so), I do not know.

Maybe this helps :slight_smile:

3 Likes

I feel your excitement, I have the same!

An interesting project to follow might be GitHub - dmjio/miso: 🍜 A tasty Haskell front-end framework. It’s currently using the old GHCJS, but I imagine they would want to update once they can (I believe they need (better) JS interop).

I’d be very interested in replacing Elm with Haskell for future things, I like Elm, but since I’ve been doing more Haskell the past years I do feel the limitations of Elm more now :sweat_smile:

1 Like

There are plans to make GHC runtime retargetable!

When we finally get there, you should be able to invoke something like ghc —target=javascript to generate javascript, with the same compiler ghc you use to generate code for your machine (provided you have an appropriate toolchain, i.e. if you wanted to generate code for an i386 target you would need to have configured a cross compilation tool chain for i386). You’ll also be able to cross-compile to other targets like wasm or aarch64-apple-darwin or x86_64-win-mingw32.

(That’s something I’m currently working on as part of Well-Typed’s GHC team)

14 Likes

Does this include the wasm backend? Right now it is a bit daunting, though I understand that to be the result of active development :slight_smile:

I failed to read the title fully. I should drink my :coffee:

1 Like

That sounds great!
Will all of Haskell be available? Or just a subpart?
I’m also wondering about the size of the generated JS bundle and other performance issues.

The runtime retargetability is orthogonal to how much of of Haskell will be available, that depends on the particular target you’re targeting :slightly_smiling_face:. So someone more involved in particular with the JS backend might be able to answer.

As @romes said. Having -target just allows you to have one compiler that can produce code for different targets, instead of having one compiler for each target. If you so wanted you cold cobble that together today with a shell script and compilers for all targets you care about. Where the shell script works as you ghc executable and dispatches to different GHCs, based on the -target flag, defaulting to native if -target is omitted.

This would be a rather hacky solution and likely require many gigabytes of diskspace. Proper -target support would be able to reused a bit more principled and less ad-hoc. It will also provide a good story for plugins as well as how Setup.hs and other build-time tools are compiled; likely requiring to make cabal (and maybe stack—I’m not particularly well informed around stacks needs here) aware of multi-target as well.

I hope this illustration helps with understanding how this relates to the targets themselves.

We try to keep the JavaScript backend in Haskell.nix rather close to the JavaScript backend development we do (including patches no in the release versions). I believe we’ll have 9.8 alphas in there soon as well.

2 Likes

The goal of GHCJS and now of the JS backend is to support as many Haskell features as possible. There are still a few gaps though: e.g. support for Compact region isn’t implemented, same for delimited continuation primops. Supporting C sources out-of-the-box is also not there yet.

Size and performance aren’t as good as they could. We’re working on reintroducing code reduction passes (especially constant and copy propagation) to help with code size. For performance, there are some low hanging fruits (e.g. https://gitlab.haskell.org/ghc/ghc/-/issues/23597) that we should fix first. Any help is welcome! :slight_smile:

2 Likes

As someone who has spent way too much time faffing about with building cross-compiling GHCs in the past two years, thank you so much for your work on this!

Someone (I think @bgamari) told me on Reddit last year that this was being targeted for GHC 9.8. Is that still on the cards, or is that now looking ambitious?

2 Likes

@george.fst unfortunately this definitely won’t make 9.8 (it’s already been forked).

This has been worked on a lot in recent years by many contributors, especially @hsyl20 and @bgamari if I’m not mistaken. I’m only continuining their work :slight_smile:

2 Likes

I spent today trying out both the JS and WASM backends. My thoughts so far on their current status are as follows:

  • The JS backend is easy enough to build, but without the capability to do foreign exports or ghcjs-dom it’s difficult to see how to do much with it.

  • The WASM backend works well on my machine, but WASI seems quite poorly supported on browsers.

All of which is a pity, since I have a project which is stuck on GHCJS, and was looking forward to upgrading it to newer GHC. Are my impressions right or wrong? Are these backends usable for anything yet, or will I have to keep on waiting?

The JS backend is easy enough to build, but without the capability to do foreign exports or ghcjs-dom it’s difficult to see how to do much with it.

You can use JS callbacks instead of foreign exports: 14. FFI and the JavaScript Backend — Glasgow Haskell Compiler 9.9.20230703 User's Guide (as in GHCJS).

All of which is a pity, since I have a project which is stuck on GHCJS, and was looking forward to upgrading it to newer GHC. Are my impressions right or wrong? Are these backends usable for anything yet, or will I have to keep on waiting?

Your impressions are right. However there is work in progress to update ghcjs-base, and then ghcjs-dom, etc. in parallel to several others (ghcup support, Cabal fixes, codegen improvement, bug fixes…). Instead of waiting it would be more productive to help. We’re not that many working on this.

1 Like

FWIW ormolu and fourmolu are both running with the WASM backend! Both projects use a third party library to provide WASI integration. Feel free to check out the source code for more details.

ormolu-live.tweag.io
fourmolu.github.io

4 Likes

Thanks, that was exactly what I was looking for! It looks like ormolu-live uses browser_wasi_shim… how did you get it to work? When I tried that, it didn’t seem to support all the WASI features required by GHC. I even reported it as an issue suggesting it should be taken off the relevant README.

Fourmolu also uses that: fourmolu/web/worker/package.json at main · fourmolu/fourmolu · GitHub

What program are you trying to run? Have you tried running a trivial program?

The program I tried to run was simply main = putStrLn "hello world". When I tried to run it with browser_wasi_shim, it gave me the following error:

async io not supported

Like I said in the linked issue, this seems due to the use of poll_oneoff in the compiled WASI file.

WASM probably doesnt support writing to stdout yet; that requires implementing (virtual) handles and such.

Fourmolu/ormolu don’t write to stdout. Try compiling a fibonacci program and running it

It works! Thank you so much!

(And as it happens my actual program already exports everything through the C FFI, so that should make it pretty easy to do stuff this way.)