Files
rose-ash/plans/erlang-on-sx.md
giles 0f67021aa3 plans: briefings + roadmaps for lua, prolog, forth, erlang, haskell
Five new guest-language plans mirroring the js-on-sx / hs-loop pattern, each
with a phased roadmap (Progress log + Blockers), a self-contained agent
briefing for respawning a long-lived loop, and a shared restore-all.sh that
snapshots state across all seven language loops.

Briefings bake in the lessons from today's stall debugging: never call
sx_build (600s watchdog), only touch lib/<lang>/** + own plan file, commit
every feature, update Progress log on each commit, route shared-file
issues to Blockers rather than fixing them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 15:16:45 +00:00

106 lines
5.4 KiB
Markdown

# Erlang-on-SX: actors on delimited continuations
The headline showcase for the SX runtime. Erlang is built around the **one** primitive — lightweight processes with mailboxes and selective receive — that delimited continuations implement natively. Most Erlang implementations ship a whole VM (BEAM) for this; on SX each process is a pair of continuations and the scheduler is ~50 lines of SX.
End-state goal: spawn a million processes, run the classic **ring benchmark**, plus a mini gen_server OTP subset and a test corpus of ~150 programs.
## Scope decisions (defaults — override by editing before we spawn)
- **Syntax:** Erlang/OTP 26 subset. No preprocessor, no parse transforms.
- **Conformance:** not BEAM-compat. "Looks like Erlang, runs like Erlang, not byte-compatible." We care about semantics, not BEAM bug-for-bug.
- **Test corpus:** custom — ring, ping-pong, fibonacci-server, bank-account-server, echo-server, plus ~100 hand-written tests for patterns/guards/BIFs. No ISO Common Test.
- **Binaries:** basic bytes-lists only; full binary pattern matching deferred.
- **Hot code reload, distribution, NIFs:** out of scope entirely.
## Ground rules
- **Scope:** only touch `lib/erlang/**` and `plans/erlang-on-sx.md`. Don't edit `spec/`, `hosts/`, `shared/`, `lib/js/**`, `lib/hyperscript/**`, `lib/lua/**`, `lib/prolog/**`, `lib/forth/**`, `lib/haskell/**`, `lib/stdlib.sx`, or `lib/` root. Erlang primitives go in `lib/erlang/runtime.sx`.
- **SX files:** use `sx-tree` MCP tools only.
- **Commits:** one feature per commit. Keep `## Progress log` updated and tick roadmap boxes.
## Architecture sketch
```
Erlang source
lib/erlang/tokenizer.sx — atoms, vars, tuples, lists, binaries, operators
lib/erlang/parser.sx — AST: modules, functions with clauses, patterns, guards
lib/erlang/transpile.sx — AST → SX AST (entry: erlang-eval-ast)
lib/erlang/runtime.sx — scheduler, processes, mailboxes, BIFs
```
Core mapping:
- **Process** = pair of delimited continuations (`on-receive`, `on-resume`) + mailbox list + pid + links
- **Scheduler** = round-robin list of runnable processes; cooperative yield on `receive`
- **`spawn`** = push a new process record, return its pid
- **`send`** = append to target mailbox; if target is blocked on receive, resume its continuation
- **`receive`** = selective — scan mailbox for first matching clause; if none, `perform` a suspend with the receive pattern; scheduler resumes when a matching message arrives
- **Pattern matching** = SX `case` on tagged values; vars bind on match
- **Guards** = side-effect-free predicate evaluated after unification
- **Immutable data** = native
- **Links / monitors / exit signals** = additional process-record fields, scheduler fires exit signals on death
## Roadmap
### Phase 1 — tokenizer + parser
- [ ] Tokenizer: atoms (bare + single-quoted), variables (Uppercase/`_`-prefixed), numbers (int, float, `16#HEX`), strings `"..."`, chars `$c`, punct `( ) { } [ ] , ; . : :: ->`
- [ ] Parser: module declarations, `-module`/`-export`/`-import` attributes, function clauses with head patterns + guards + body
- [ ] Expressions: literals, vars, calls, tuples `{...}`, lists `[...|...]`, binaries `<<...>>`, `if`, `case`, `receive`, `fun`, `try/catch`, operators
- [ ] Unit tests in `lib/erlang/tests/parse.sx`
### Phase 2 — sequential eval + pattern matching + BIFs
- [ ] `erlang-eval-ast`: evaluate sequential expressions
- [ ] Pattern matching (atoms, numbers, vars, tuples, lists, `[H|T]`, underscore, bound-var re-match)
- [ ] Guards: `is_integer`, `is_atom`, `is_list`, `is_tuple`, comparisons, arithmetic
- [ ] BIFs: `length/1`, `hd/1`, `tl/1`, `element/2`, `tuple_size/1`, `atom_to_list/1`, `list_to_atom/1`, `lists:map/2`, `lists:foldl/3`, `lists:reverse/1`, `io:format/1-2`
- [ ] 30+ tests in `lib/erlang/tests/eval.sx`
### Phase 3 — processes + mailboxes + receive (THE SHOWCASE)
- [ ] Scheduler in `runtime.sx`: runnable queue, pid counter, per-process state record
- [ ] `spawn/1`, `spawn/3`, `self/0`
- [ ] `!` (send), `receive ... end` with selective pattern matching
- [ ] `receive ... after Ms -> ...` timeout clause (use SX timer primitive)
- [ ] `exit/1`, basic process termination
- [ ] Classic programs in `lib/erlang/tests/programs/`:
- [ ] `ring.erl` — N processes in a ring, pass a token around M times
- [ ] `ping_pong.erl` — two processes exchanging messages
- [ ] `bank.erl` — account server (deposit/withdraw/balance)
- [ ] `echo.erl` — minimal server
- [ ] `fib_server.erl` — compute fib on request
- [ ] `lib/erlang/conformance.sh` + runner, `scoreboard.json` + `scoreboard.md`
- [ ] Target: 5/5 classic programs + 1M-process ring benchmark runs
### Phase 4 — links, monitors, exit signals
- [ ] `link/1`, `unlink/1`, `monitor/2`, `demonitor/1`
- [ ] Exit-signal propagation; trap_exit flag
- [ ] `try/catch/of/end`
### Phase 5 — modules + OTP-lite
- [ ] `-module(M).` loading, `M:F(...)` calls across modules
- [ ] `gen_server` behaviour (the big OTP win)
- [ ] `supervisor` (simple one-for-one)
- [ ] Registered processes: `register/2`, `whereis/1`
### Phase 6 — the rest
- [ ] List comprehensions `[X*2 || X <- L]`
- [ ] Binary pattern matching `<<A:8, B:16>>`
- [ ] ETS-lite (in-memory tables via SX dicts)
- [ ] More BIFs — target 200+ test corpus green
## Progress log
_Newest first._
- _(not started)_
## Blockers
- _(none yet)_