Skip to main content

WebAssembly Integration Guide

AILANG can run entirely in the browser using WebAssembly, enabling interactive demonstrations and online playgrounds without requiring server-side execution.

See WASM in Action

All AILANG browser demos run on WebAssembly — try them live:

Overview​

The AILANG WebAssembly build provides:

  • Full Language Support: Complete AILANG interpreter compiled to WASM
  • Standard Library Included: All 20 stdlib modules (std/list, std/json, etc.) embedded and auto-loaded
  • Client-Side Execution: No server needed after initial load
  • Small Bundle Size: ~33MB uncompressed (~7MB with gzip)
  • React Integration: Ready-made component for easy integration
  • Offline Capable: Works offline after first load

Quick Start​

Download the pre-built WASM bundle from the latest release. All three files are version-matched and built from the same commit:

# Download and extract — includes ailang.wasm, wasm_exec.js, ailang-repl.js
curl -L https://github.com/sunholo-data/ailang/releases/latest/download/ailang-wasm.tar.gz | tar -xz

The archive contains:

FileSizeDescription
ailang.wasm~33MBCompiled WASM binary with embedded stdlib
wasm_exec.js~17KBGo WASM runtime (version-matched to build toolchain)
ailang-repl.js~5KBJavaScript wrapper class (AilangREPL)

Option 2: Build from Source​

cd ailang
make build-wasm

# Copy wasm_exec.js from your Go installation
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

This produces bin/ailang.wasm. You'll also need ailang-repl.js from the web/ directory.

Integration Options​

  1. Copy assets:
cp ailang.wasm docs/static/wasm/
cp wasm_exec.js docs/static/wasm/
cp ailang-repl.js docs/src/components/
cp web/AilangRepl.jsx docs/src/components/
  1. Add to docusaurus.config.js:
module.exports = {
scripts: [
{
src: '/wasm/wasm_exec.js',
async: false,
},
],
// ... rest of config
};
  1. Use in MDX:
---
title: Try AILANG
---

import AilangRepl from '@site/src/components/AilangRepl';

<AilangRepl />

Option B: Vanilla HTML​

<!DOCTYPE html>
<html>
<head>
<title>AILANG REPL</title>
<script src="wasm_exec.js"></script>
<script src="ailang-repl.js"></script>
</head>
<body>
<div id="repl-container"></div>

<script>
const repl = new AilangREPL();

repl.init('/path/to/ailang.wasm').then(() => {
console.log('AILANG ready!');

// Evaluate expressions
const result = repl.eval('1 + 2');
console.log(result); // "3 :: Int"
});
</script>
</body>
</html>

Option C: React (Custom)​

import { useEffect, useState } from 'react';
import AilangREPL from './ailang-repl';

export default function MyReplComponent() {
const [repl, setRepl] = useState(null);
const [result, setResult] = useState('');

useEffect(() => {
const replInstance = new AilangREPL();
replInstance.init('/wasm/ailang.wasm').then(() => {
setRepl(replInstance);
});
}, []);

const handleEval = (input) => {
if (repl) {
const output = repl.eval(input);
setResult(output);
}
};

return (
<div>
<input onKeyDown={(e) => {
if (e.key === 'Enter') handleEval(e.target.value);
}} />
<pre>{result}</pre>
</div>
);
}

JavaScript API​

AilangREPL Class​

const repl = new AilangREPL();

Methods​

init(wasmPath)​

Initialize the WASM module.

await repl.init('/wasm/ailang.wasm');

Parameters:

  • wasmPath (string): Path to ailang.wasm file

Returns: Promise that resolves when REPL is ready

eval(input)​

Evaluate an AILANG expression.

const result = repl.eval('1 + 2');
// Returns: "3 :: Int"

Parameters:

  • input (string): AILANG code to evaluate

Returns: Result string (includes value and type)

command(cmd)​

Execute a REPL command.

const type = repl.command(':type \x. x');
// Returns: "\x. x :: a -> a"

Parameters:

  • cmd (string): REPL command (e.g., :type, :help)

Returns: Command output string

reset()​

Reset the REPL environment.

repl.reset();

Returns: Status message

onReady(callback)​

Register callback for when REPL is ready.

repl.onReady(() => {
console.log('REPL initialized!');
});
getVersion()​

Get version information.

const version = repl.getVersion();
// Returns: "v0.7.2" (or null if not ready)

Returns: Version string or null

getVersionInfo()​

Get detailed version information.

const info = repl.getVersionInfo();
// Returns: { version: "v0.7.2", buildTime: "2026-02-06T...", platform: "js/wasm" }

Returns: Object with version, buildTime, platform fields, or null

needsContinuation(line)​

Check if a line needs continuation (for multi-line input UIs).

repl.needsContinuation('let x = 5 in'); // true
repl.needsContinuation('1 + 2'); // false

Returns: boolean

Module Loading API (v0.7.1+)​

The WASM REPL supports loading complete AILANG modules, enabling complex browser-based demos with multiple function definitions.

Why Module Loading?

The REPL evaluates expressions line-by-line, so function definitions like let add = \x. \y. x + y don't persist in scope for subsequent calls. The Module Loading API solves this by compiling entire modules and storing their exports for later use.

loadModule(name, code)​

Load an AILANG module into the registry.

const result = repl.loadModule('math', `
let add: Int -> Int -> Int = \\x. \\y. x + y
let mul: Int -> Int -> Int = \\x. \\y. x * y
`);

if (result.success) {
console.log('Exports:', result.exports);
// Exports: ["add", "mul"]
} else {
console.error('Error:', result.error);
}

Parameters:

  • name (string): Module name (e.g., "math", "invoice_processor")
  • code (string): AILANG source code

Returns: Object with:

  • success (boolean): Whether loading succeeded
  • exports (string[]): List of exported function names (on success)
  • error (string): Error message (on failure)
Type Annotations Required

For functions with numeric operations, you must include explicit type annotations:

// ✅ Works
let add: Int -> Int -> Int = \\x. \\y. x + y

// ❌ Fails with "ambiguous type variable"
let add = \\x. \\y. x + y
Export Behavior (v0.7.1.2+)

Modules can use explicit exports or export all bindings:

Explicit exports (recommended for libraries):

// Only public_func is exported, private_func is hidden
repl.loadModule('mylib', `
module mylib
export pure func public_func(x: int) -> int = x * 2
pure func private_func(x: int) -> int = x * 3
`);
// result.exports: ["public_func"]

Implicit exports (for REPL-style code):

// All top-level bindings are exported
repl.loadModule('utils', `
let double: Int -> Int = \\x. x * 2
let triple: Int -> Int = \\x. x * 3
`);
// result.exports: ["double", "triple"]
listModules() (v0.7.1+)​

List all loaded modules.

const modules = repl.listModules();
// Returns: ["math", "utils"]

Returns: Array of module names

importModule(moduleName) (v0.7.1+)​

Import a module's exports into the REPL environment.

repl.importModule('math');
// Now you can use: repl.eval('add(2)(3)')

Parameters:

  • moduleName (string): Name of a loaded module

Returns: Import result message

call(moduleName, funcName, ...args)​

Call a function from a loaded module. This is a convenience method that:

  1. Looks up the function from the module registry
  2. Converts JavaScript arguments to AILANG values
  3. Invokes the function directly (no eval)
// Load a module
repl.loadModule('math', `
let add: Int -> Int -> Int = \\x. \\y. x + y
let greet = \\name. "Hello, " <> name
`);

// Call functions
const sum = repl.call('math', 'add', 2, 3);
if (sum.success) {
console.log(sum.result); // "5 :: Int"
}

const greeting = repl.call('math', 'greet', 'World');
if (greeting.success) {
console.log(greeting.result); // "\"Hello, World\" :: String"
}

Parameters:

  • moduleName (string): Module containing the function
  • funcName (string): Function to call
  • ...args (any): Arguments (converted to AILANG values)

Returns: Object with:

  • success (boolean): Whether the call succeeded
  • result (string): Result with value and type (on success)
  • error (string): Error message (on failure)

Supported argument types:

JavaScript TypeAILANG Syntax
number42 or 3.14
string"text"
booleantrue / false
array[1, 2, 3]

Module Loading Example​

Here's a complete example building an invoice processor demo:

<!DOCTYPE html>
<html>
<head>
<title>Invoice Processor Demo</title>
<script src="wasm_exec.js"></script>
<script src="ailang-repl.js"></script>
</head>
<body>
<script>
const repl = new AilangREPL();

repl.init('/wasm/ailang.wasm').then(() => {
// Load the invoice processor module
const result = repl.loadModule('invoice', `
-- Invoice processing functions
let calculateTotal: Int -> Float -> Float = \\quantity. \\unitPrice.
int_to_float(quantity) * unitPrice

let applyDiscount: Float -> Float -> Float = \\total. \\discountPercent.
total * (1.0 - discountPercent / 100.0)

let formatCurrency: Float -> String = \\amount.
"$" <> float_to_string(amount)
`);

if (!result.success) {
console.error('Failed to load module:', result.error);
return;
}

console.log('Loaded exports:', result.exports);
// ["calculateTotal", "applyDiscount", "formatCurrency"]

// Process an invoice
const subtotal = repl.call('invoice', 'calculateTotal', 5, 19.99);
if (subtotal.success) {
console.log('Subtotal:', subtotal.result);
// "99.95 :: Float"
}

const total = repl.call('invoice', 'applyDiscount', 99.95, 10);
if (total.success) {
console.log('After 10% discount:', total.result);
// "89.955 :: Float"
}

// Or use eval directly after importing
repl.importModule('invoice');
const formatted = repl.eval('formatCurrency(89.95)');
console.log('Formatted:', formatted);
// "\"$89.95\" :: String"
});
</script>
</body>
</html>

Effect Handlers API (v0.7.1+)​

The WASM REPL supports configuring AILANG's algebraic effect system from JavaScript. This enables browser-based programs to use effects like AI, IO, Net, FS, and more by providing JavaScript callback implementations.

Why Effect Handlers?

AILANG tracks side effects in the type system. A function declared as func ask(q: string) -> string ! {AI} requires the AI capability to run. In the CLI, capabilities are granted via --caps. In the browser, you provide JavaScript implementations for each effect operation.

Low-Level Globals​

These functions are registered on the global window object when the WASM module loads:

Global FunctionPurpose
ailangSetEffectHandler(name, ops)Override any effect with JS callbacks
ailangSetAIHandler(fn)Convenience wrapper for AI effect
ailangGrantCapability(name)Grant a capability without a handler
ailangEvalAsync(expr)Evaluate with async effect support
ailangCallAsync(mod, fn, ...args)Call module function with async effects

AilangREPL Wrapper Methods​

The AilangREPL class wraps all low-level globals with error handling:

const repl = new AilangREPL();
await repl.init('/wasm/ailang.wasm');

// Register effect handlers
repl.setEffectHandler('IO', 'print', (msg) => { output.textContent += msg; });
repl.setAIHandler(async (input) => { /* call LLM API */ });
repl.grantCapability('IO');

// Async evaluation (returns Promise)
const result = await repl.evalAsync('perform IO.print("hello")');
const output = await repl.callAsync('demo', 'processData', 'input');
Wrapper vs Low-Level API

The wrapper methods (repl.setEffectHandler()) provide error handling and readiness checks. The low-level globals (ailangSetEffectHandler()) are called directly on window and skip these checks. For most use cases, prefer the wrapper methods.

ailangSetEffectHandler(effectName, handlers)​

Override any effect's operations with JavaScript callbacks. Auto-grants the named capability.

// IO effect — route print/println to DOM
ailangSetEffectHandler("IO", {
print: (msg) => { output.textContent += msg; },
println: (msg) => { output.textContent += msg + '\n'; },
readLine: () => prompt("Enter input:")
});

// Net effect — use browser fetch
ailangSetEffectHandler("Net", {
httpRequest: async (method, url, headers, body) => {
const resp = await fetch(url, {
method,
headers: JSON.parse(headers),
body
});
return JSON.stringify({
status: resp.status,
body: await resp.text(),
ok: resp.ok
});
}
});

// Clock effect — use JS Date
ailangSetEffectHandler("Clock", {
now: () => Date.now(),
sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms))
});

Parameters:

  • effectName (string): Effect name matching the AILANG capability (e.g., "IO", "AI", "Net")
  • handlers (object): Map of operation names to JS functions

Returns: { success: true } or { success: false, error: "..." }

Supported handler return types:

  • Synchronous values (string, number, boolean, null)
  • Promises (resolved value is converted to AILANG)

ailangSetAIHandler(callback)​

Convenience wrapper for the AI effect. Sets both the effect handler and the dedicated AIHandler on the effect context. This is the recommended way to enable std/ai.call() in the browser.

// Connect to Gemini Flash API
ailangSetAIHandler(async function(input) {
const resp = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: input }] }]
})
}
);
const data = await resp.json();
return data.candidates[0].content.parts[0].text;
});

Parameters:

  • callback (function): JS function that takes a string and returns a string (or Promise<string>)

Returns: { success: true } or { success: false, error: "..." }

Auto-Grant

ailangSetAIHandler automatically grants the AI capability and registers the handler in the effect registry. You don't need to call ailangGrantCapability("AI") separately.

ailangGrantCapability(name)​

Grant a named capability without providing a handler. Useful when the effect operations are already registered (e.g., built-in effects that work in WASM).

ailangGrantCapability("IO");
ailangGrantCapability("Debug");

Parameters:

  • name (string): Capability name

Returns: { success: true } or { success: false, error: "..." }

Valid Capabilities​

CapabilityDescriptionCommon Operations
IOConsole/display I/Oprint, println, readLine
FSFile system accessreadFile, writeFile, exists
NetNetwork requestshttpGet, httpPost, httpRequest
AIAI model completioncomplete
ClockTime operationsnow, sleep

ailangEvalAsync(expression)​

Evaluate an AILANG expression asynchronously. Returns a JavaScript Promise. Required when the expression may trigger effect handlers that return Promises (e.g., AI calls, network requests).

// Sync handler — ailangEval() works fine
ailangSetAIHandler((input) => "mock: " + input);
const result = repl.eval('import std/ai as AI in AI.call("hello")');

// Async handler — MUST use ailangEvalAsync
ailangSetAIHandler(async (input) => {
const resp = await fetch('/api/ai', { method: 'POST', body: input });
return await resp.text();
});
const result = await ailangEvalAsync('import std/ai as AI in AI.call("hello")');

Parameters:

  • expression (string): AILANG expression or REPL command

Returns: Promise<string> — resolves with result string, rejects on error

ailangCallAsync(moduleName, funcName, ...args)​

Call a module export asynchronously. Returns a JavaScript Promise. Same as repl.call() but supports async effect handlers.

// Load a module that uses AI
repl.loadModule('demo', `
module demo
import std/ai as AI

export func askAI(question: string) -> string ! {AI} =
AI.call(question)
`);

// Call with async AI handler
ailangSetAIHandler(async (input) => {
// ... fetch from LLM API
return "AI response";
});

const result = await ailangCallAsync('demo', 'askAI', 'What is 2+2?');
// result: { success: true, result: '"AI response" :: String' }

Parameters:

  • moduleName (string): Module containing the function
  • funcName (string): Function to call
  • ...args (any): Arguments (number, string, boolean)

Returns: Promise<{ success, result?, error? }>

Effect Handler Example​

Complete example using AI and IO effects in the browser:

<!DOCTYPE html>
<html>
<head>
<title>AILANG Effects Demo</title>
<script src="wasm_exec.js"></script>
<script src="ailang-repl.js"></script>
</head>
<body>
<div id="output"></div>

<script>
const repl = new AilangREPL();
const output = document.getElementById('output');

repl.init('/wasm/ailang.wasm').then(async () => {
// Set up IO effect — route output to DOM
ailangSetEffectHandler("IO", {
print: (msg) => { output.textContent += msg; },
println: (msg) => { output.textContent += msg + '\n'; }
});

// Set up AI effect — mock handler for demo
ailangSetAIHandler((input) => {
return "The answer is 42";
});

// Load a module that uses both effects
repl.loadModule('demo', `
module demo
import std/ai as AI

export func greet(name: string) -> () ! {IO} =
println("Hello, ${name}!")

export func ask(question: string) -> string ! {AI} =
AI.call(question)
`);

// Call IO function (sync is fine for sync handlers)
repl.call('demo', 'greet', 'World');
// DOM shows: "Hello, World!"

// Call AI function
const answer = await ailangCallAsync('demo', 'ask', 'What is the meaning of life?');
console.log(answer.result);
// '"The answer is 42" :: String'
});
</script>
</body>
</html>

Complete Method Reference​

MethodReturnsSinceDescription
init(wasmPath)Promise<this>v0.7.0Initialize WASM module
eval(input)stringv0.7.0Evaluate expression
command(cmd)stringv0.7.0Execute REPL command
reset()stringv0.7.0Reset environment + reload stdlib
getVersion()string or nullv0.7.0Get version string
getVersionInfo()Object or nullv0.7.0Get version, buildTime, platform
needsContinuation(line)booleanv0.7.0Check if line needs more input
onReady(callback)voidv0.7.0Register ready callback
loadModule(name, code){success, exports?, error?}v0.7.1Compile and register module
listModules()string[]v0.7.1List loaded module names
call(mod, func, ...args){success, result?, error?}v0.7.1Call module export
importModule(name)stringv0.7.1Import into REPL env
setEffectHandler(cap, op, fn){success, error?}v0.7.2Register effect handler
setAIHandler(fn){success, error?}v0.7.2Register AI handler
grantCapability(cap){success, error?}v0.7.2Grant effect capability
evalAsync(input)Promise<string>v0.7.2Async eval (for effects)
callAsync(mod, func, ...args)Promise<{success, result?, error?}>v0.7.2Async call (for effects)

Cognitive OS Substrate (v0.21.x)​

The WASM build ships the Cognitive OS substrate — a deterministic agent runtime layered on top of the standard WASM REPL. Agents declare effects, mutate scoped DOM regions, exchange messages, and produce replayable event logs.

The Go-side substrate is complete and shippable (M-COG-RUNTIME, see design doc). The browser-side host JS that lights up the bridges is a follow-up sprint (M-COG-RUNTIME-BROWSER) — once it ships, the JS APIs below become directly usable end-to-end.

Effects shipped​

EffectPurposeStdlib module
DOMScoped, structured browser-DOM mutation (no raw HTML/JS injection)std/dom
MsgInter-agent messaging fabric — same code over LocalWorker / BroadcastChannelstd/cognition
TraceCognitive event emission (already shipped v0.11.1; extension lands in M-COG-RUNTIME-BROWSER)std/trace

Step-pattern interface​

DOM and Msg handlers follow the step-pattern interface established by AI:

  • DOMHandler: ApplyPatch / ApplyBatch / Subscribe
  • MsgHandler: Send / Recv / Subscribe

Each handler ships with a deterministic Stub*Handler for testing and Result-returning op variants (applyPatchResult etc.) that produce typed Result[T, {code, message}] for graceful AILANG-side error handling.

Cognitive event log​

Every effect call writes a typed event to the cognitive event log:

Event kindSource
MessageSentMsg.send
MessageReceivedMsg.recv
PatchAppliedDOM.applyPatch / DOM.applyBatch
CapabilityExceededBudget overrun (M-COG-RUNTIME-BROWSER wires this)
TraceCapturedTrace span completion (M-COG-RUNTIME-BROWSER ext)

The log exports to JSONL for deterministic replay across machines. The Replayer re-feeds events through a fresh scheduler in canonical (LamportClock, Sender) order and produces byte-identical dispatch slices — the "deterministic distributed replay" property the umbrella design doc names as the strategic claim of the Cognitive OS.

JavaScript API (Cognitive OS bridges)​

The browser-side host JS registers callbacks for each effect; the WASM bridge invokes them from Go:

// DOM
ailangSetDOMApplyPatchHandler((region, patch) => {
// patch = {ctor: "AddPanel", fields: ["title", "content"]} | UpdateNode | RemoveNode | AddTimeline
// Apply to <div data-cog-region="${region}"> and return {node_id, budget_remaining}
});
ailangSetDOMApplyBatchHandler((region, patches) => { /* ... */ });

// Msg (transport-independent)
ailangSetMsgSendHandler((to, payload) => {
// postMessage on BroadcastChannel(to) or send over WebSocket
return {msg_id, clock, budget_remaining};
});
ailangSetMsgRecvHandler((mailbox) => {
// Pull the next envelope; can return a Promise for async transports
return {msg_id, from, to, payload, clock};
});

Example: replayable DOM mutation​

module myagent

import std/dom (DOMPatch, AddPanel, applyPatchResult)
import std/cognition (sendMsgResult)
import std/result (Result, Ok, Err)

export func renderHeatmap(region: string) -> string ! {DOM} = {
match applyPatchResult(region, AddPanel("Status", "Running...")) {
Ok(r) => "node:${r.node_id}",
Err(e) => "failed:${e.code}"
}
}

export func relay(to: string, payload: string) -> string ! {Msg} = {
match sendMsgResult(to, payload) {
Ok(r) => "sent:${r.msg_id}@${show(r.clock)}",
Err(e) => "send_failed:${e.code}"
}
}

A full runnable demo lives at examples/cognitive_os/single_agent_replay.ail.

Status​

ComponentStatus
DOM / Msg effect labels in type system✅ Shipped (v0.21.x)
Step-pattern handler interfaces + stubs✅ Shipped
Result-returning op variants✅ Shipped
AILANG stdlib bindings (std/dom, std/cognition)✅ Shipped
WASM bridges (8 JS globals: ailangSetDOM*Handler, ailangSetMsg*Handler)✅ Shipped
Lamport clock + cognitive event log + JSONL replay✅ Shipped
Transport trait + LocalWorker + BroadcastChannel✅ Shipped
Deterministic scheduler + replay engine✅ Shipped
NativeMsgHandler wrapping Transport (non-WASM)✅ Shipped
Browser host JS (host.js, replay.js, canonical_dom.js)⏳ M-COG-RUNTIME-BROWSER
IndexedDB persistence sink⏳ M-COG-RUNTIME-BROWSER
Subscribe ops wired through FnCaller to AILANG closures⏳ M-COG-RUNTIME-BROWSER
!: SharedMem + !: SemanticSearch (semantic memory)⏳ M-COG-MEMORY (v0.22.0)
WebSocket / FirestoreRelay / WebRTC transports⏳ M-COG-MESH (v0.22.x → v0.23)
Collaborative 4-agent demo⏳ M-COG-MESH

REPL Commands​

The WebAssembly REPL supports the same commands as the CLI:

CommandDescription
:helpShow available commands
:type <expr>Display expression type
:instancesShow type class instances
:resetClear environment

Limitations​

The browser version has these limitations compared to the CLI:

FeatureCLIWASM
Expression evaluationYesYes
Type inferenceYesYes
Pattern matchingYesYes
Type classesYesYes
Module loadingYesYes (v0.7.1+)
Standard libraryYesYes (v0.7.1+)*
Explicit exportsYesYes (v0.7.1.2+)
Effect handlersYes (--caps)Yes (v0.7.1+, via JS callbacks)
AI effect (std/ai)YesYes (v0.7.1+, via ailangSetAIHandler)
IO effect (print/read)Yes (terminal)Yes (v0.7.1+, via JS callbacks)
Net effect (HTTP)YesVia JS callbacks (CORS applies)
FS effect (files)Yes (filesystem)Via JS callbacks (localStorage/IndexedDB)
Async evaluationN/AYes (v0.7.1+, ailangEvalAsync)
Custom file importsYesNo (use loadModule())
History persistenceYesNo

* The standard library (20 modules including std/list, std/json, std/string) is embedded in the WASM binary and auto-loaded on init. Use importModule('std/list') to bring stdlib exports into scope. Custom user file imports require loadModule() instead.

Deployment​

Static Hosting​

WASM files work on any static host:

# Build and deploy
make build-wasm
cp bin/ailang.wasm your-site/static/wasm/
# Deploy your-site/ to Netlify/Vercel/GitHub Pages

CDN Optimization​

  1. Enable Compression:
# nginx.conf
gzip_types application/wasm;
  1. Set Cache Headers:
location ~* \.wasm$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
  1. Use HTTP/2: WASM benefits from HTTP/2 multiplexing for faster loading.

Performance Tips​

  • Lazy Loading: Only load WASM when user navigates to playground
  • Service Worker: Cache WASM for offline use
  • CDN: Serve from edge locations
  • Preload: Add <link rel="preload" href="ailang.wasm" as="fetch">

CI/CD Integration​

GitHub Actions​

The release workflow automatically builds and bundles the WASM archive with all dependencies:

# .github/workflows/release.yml (excerpt)
- name: Build WASM binary
run: make build-wasm

- name: Bundle WASM archive with JS dependencies
run: |
# Get version-matched wasm_exec.js from Go toolchain
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" bin/
cp web/ailang-repl.js bin/
cd bin && tar -czf ailang-wasm.tar.gz ailang.wasm wasm_exec.js ailang-repl.js

The archive is verified (size checks for all 3 files) and attached to the GitHub Release as ailang-wasm.tar.gz.

Docusaurus Deployment​

WASM is included in documentation builds:

# .github/workflows/docusaurus-deploy.yml (excerpt)
- name: Build WASM binary
run: make build-wasm

- name: Copy static assets
run: |
cp bin/ailang.wasm docs/static/wasm/
cp web/ailang-repl.js docs/src/components/

Troubleshooting​

"WebAssembly not supported"​

Solution: Use a modern browser:

  • Chrome 57+
  • Firefox 52+
  • Safari 11+
  • Edge 16+

"Failed to load AILANG WASM"​

Solutions:

  1. Check browser console for network errors
  2. Verify ailang.wasm path is correct
  3. Ensure wasm_exec.js loaded first
  4. Check CORS headers if serving from different domain

"REPL not initialized"​

Solution: Wait for init() promise or use onReady():

repl.init('/wasm/ailang.wasm').then(() => {
// Safe to use repl here
repl.eval('1 + 2');
});

Slow Loading​

Solutions:

  1. Enable gzip compression (reduces to ~1-2MB)
  2. Use CDN
  3. Add preload hints:
    <link rel="preload" href="/wasm/ailang.wasm" as="fetch" crossorigin>

Effects Don't Work​

Solutions:

  1. Grant the capability first: repl.grantCapability('IO')
  2. Register handlers: repl.setEffectHandler('IO', 'print', fn)
  3. Use async methods if handlers return Promises: await repl.evalAsync(...)
  4. Check that ailangSetAIHandler was called before AI.call() — it auto-grants the AI capability

"undefined global variable: X from std/Y"​

Cause: Module's stdlib import failed during initialization.

Solution: Check browser console for warnings during stdlib loading. Try repl.reset() to reload the standard library.

Module Loading Errors​

"ambiguous type variable α with classes [Num]"​

Cause: Functions with numeric operations need explicit type annotations.

Solution: Add type annotations to function definitions:

// ❌ Fails
let add = \\x. \\y. x + y

// ✅ Works
let add: Int -> Int -> Int = \\x. \\y. x + y

"module X not loaded (use loadModule first)"​

Cause: Trying to call or import a module that hasn't been loaded.

Solution: Load the module before using it:

// First load
repl.loadModule('mymodule', code);
// Then import or call
repl.importModule('mymodule');

"parse error" or "type error" from loadModule​

Cause: Invalid AILANG syntax or type errors in module code.

Solution: Check the error message and fix the module code:

const result = repl.loadModule('test', badCode);
if (!result.success) {
console.error('Module error:', result.error);
// e.g., "parse error: expected expression at line 3"
}

Examples​

See:

Next Steps​