Repository navigation
[#446 Tier 1 #5] WASM-exports calling pattern (recommended kickoff) #455
Description
Part of #446 — Tier 1 item 5. Recommended kickoff per roadmap memory.
Goal
Ship the WASM-exports calling pattern as the first concrete binding from the top-50 roadmap. Lowest scope, highest leverage:
- ~30 extern fns OR one generic
wasm_export_call(design decision) - < 1 day estimated
- Unlocks idaptik vm/wasm AND every future WASM-host integration
Background
stdlib/Deno.affine has WasmExports type + wasmInstance. What it lacks is the calling pattern for the exports of a loaded wasm instance — i.e., once you've loaded mywasm.wasm, how do you call mywasm.exports.add(1, 2) from AffineScript?
Two design options:
Option A: extern fns per type signature
Generate ~30 extern fns covering common signatures:
wasm_call_0args_returns_i32wasm_call_1arg_i32_returns_i32wasm_call_2args_i32_i32_returns_i32- ... etc.
Pros: type-safe; AS typechecker knows what's allowed.
Cons: bloats Deno.affine; doesn't cover unusual signatures.
Option B: single generic wasm_export_call
extern fn wasm_export_call(
exports: WasmExports,
name: String,
args: Array<WasmValue>
): WasmValue
Where WasmValue is a sum of I32 | I64 | F32 | F64.
Pros: single binding; covers any signature.
Cons: weaker type-safety at the AS call-site (user must marshal manually).
Owner decision needed in PR review — recommendation: ship Option B first (smaller, covers everything), then add Option A overloads as Deno.affine mature.
Acceptance
Deno.affineexports awasm_export_call(or per-signature variants)- Idris2 ABI pattern documented (per estate convention: Zig=APIs/FFIs, Idris2=ABIs)
- Zig FFI implementation lives in
affinescript-stdlib/ffi/zig/(or wherever current pattern dictates) - At least one consumer-side example: e.g.,
examples/wasm-exports-demo.affinethat loads a tiny wasm and invokes an export
Test plan
- Build green:
dune build(or whatever the AS build command is — confirm at start) - Example runs end-to-end with
deno run -A examples/wasm-exports-demo.ts(or AS equivalent) - Idaptik vm/wasm consumer team verifies the binding shape works for their use case
Sequencing
- BLOCKED ON: nothing — this is the kickoff.
- BLOCKS: every future Tier 1 item that needs to interact with loaded wasm modules (idaptik vm/wasm, Phoenix WebSocket framing if it composes a wasm marshalling step, etc.)
Cross-references
- Memory:
project_affinescript_bindings_top50_roadmap.md— "Highest-leverage starting points" §1 - Umbrella: [campaign] AffineScript top-50 framework bindings roadmap (UMBRELLA) #446
Activity
hyperpolymath commentedon May 30, 2026
Decision: Option B — single generic
wasm_export_call(exports, name, args: Array<WasmValue>): WasmValue.Why B
Deno.affine(one extern fn + one sum type) vs ~30 near-identical declarations.wasm_export_calllater as ergonomic helpers. Picking A would delay the generic; picking B doesn't delay typed wrappers (they sit on top).Acknowledged trade-off
Weaker static safety at the call site — user marshals manually to
WasmValue(I32 | I64 | F32 | F64). Errors deferred to runtime. Acceptable for the kickoff binding; typed wrappers will land as a follow-up sub-issue once usage patterns crystallise.Implementation scope
Deno.affine: addWasmValuesum type +wasm_export_callextern declarationexamples/wasm-exports-demo.affineloading a tiny wasm + invoking an export end-to-endReady for a dedicated implementation session.
feat(stdlib): WasmValue + wasm_export_call typed wasm-exports binding (…
docs(bindings-roadmap): fill in Tracking section with live issue link…
docs(bindings-roadmap): fill in Tracking section with live issue link…
Roadmap tracking + Zig-FFI doc (#19) + hex/base64 encoding (#25) + In…
feat(stdlib): Ipc.affine — MessageChannel + structuredClone (bindings #9
docs(examples): wasm-exports-demo.affine + smoke harness (closes #500)