A JS backend is for web apps, which makes object mapping the right shape

This commit is contained in:
Joseph Ferano 2026-09-12 13:58:29 +07:00
parent c49286ca01
commit 99db8ce73f

View File

@ -154,15 +154,36 @@ See item 7.
Also unresolved: the shim generates a C typedef per *named* struct, and an anonymous struct has no name to generate one
from. Either anonymous structs cannot cross the FFI, or the generator learns to name them.
## 5. A performant JS transpiler
## 5. A JS backend — for web apps, not for games
Asked as a feasibility question. Bear in mind the web target already exists and ships real machine code via wasm, so this
is not the only route to a browser and the case for it needs stating: smaller artifacts, no wasm toolchain, debuggability
in browser devtools, or something else.
**The goal, stated in conversation: calling JS libraries, and eventually a hiccup-style DSL for writing web apps in
Flan.** That is a different product from the wasm target and the two do not compete — games go to wasm, web apps go to
JS.
The hard parts are the ones the wasm target got for free from clang: the memory model (Flan is pointers and explicit
layout; JS is not), the FFI, and the fact that the whole raylib layer is C. A JS backend that cannot run raylib is a
different product from the one that can.
This matters because the obvious objection does not apply. "A JS backend cannot call raylib, so the whole graphics layer
would need reimplementing on canvas" is fatal for a *game* backend and irrelevant here: web apps do not use raylib.
**The design fork, and it should be decided first:**
- **Linear memory** — an `ArrayBuffer` standing in for pointers and structs, with typed-array reads and writes. Fast
arithmetic, and every value is opaque bytes, so every call into a JS library marshals both ways. This is asm.js, which
wasm exists to replace, and it is precisely wrong for an interop-motivated backend.
- **Object mapping** — a Flan struct becomes a plain JS object, a `Vec` becomes a JS array. Slower for tight numeric
loops, natural for interop, readable output. **For this goal this is clearly the right one**, and performance is not
the constraint people assume: DOM work is dominated by the browser, not by arithmetic.
**The consequence to face early: the memory model does not come along.** Object mapping means garbage collection, which
means no pointers, no manual `free`, no arena, no allocator. So this is a **dialect**, not merely a second backend —
some Flan programs will not compile to JS, and that should be named up front rather than discovered. Worth deciding
which subset is the JS-targetable one, and whether the checker enforces it per target.
**The unexpected upside: the dev loop could work on the web.** Conditions, restarts and a break loop all need the
ability to run new code in a stopped program. wasm cannot have this — `--dev` is refused there because reload is
`dlopen` and the browser has no sockets. JS has no such problem: it evaluates new code trivially. A JS target might be
the *only* place the live dev loop and the browser coexist.
**One dependency:** hiccup is a macro, and the macro expander is blocked on `Form` being a Flan union, which is union
values. The backend can start before that; the DSL cannot.
## 6. C interop as seamless as Zig's