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};
});

For the underlying Go-side primitives (Lamport clock, transport trait, scheduler, replay engine), see the internal/cognition/ package.

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