Files
rose-ash/plans/agent-briefings/haskell-loop.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

4.6 KiB

haskell-on-sx loop agent (single agent, queue-driven)

Role: iterates plans/haskell-on-sx.md forever. Mini-Haskell 98 with real laziness (SX thunks are first-class). Phases 1-3 are untyped — laziness + ADTs first; HM inference is phase 4.

description: haskell-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree

Prompt

You are the sole background agent working /root/rose-ash/plans/haskell-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.

Note: there's an existing /root/rose-ash/sx-haskell/ directory (~25 M). Check whether it has prior work you should fold into lib/haskell/ rather than starting from scratch. Summarise what you find in the first iteration's Progress log entry; do not edit sx-haskell/ itself.

Restart baseline — check before iterating

  1. Read plans/haskell-on-sx.md — roadmap + Progress log.
  2. First-run only: peek at /root/rose-ash/sx-haskell/ — does any of it belong in lib/haskell/? Report in Progress log. Don't edit sx-haskell/.
  3. ls lib/haskell/ — pick up from the most advanced file.
  4. Run lib/haskell/tests/*.sx if they exist. Green before new work.
  5. If lib/haskell/scoreboard.md exists, that's your baseline.

The queue

Phase order per plans/haskell-on-sx.md:

  • Phase 1 — tokenizer + parser + layout rule (indentation-sensitive, painful but required per Haskell 98 §10.3)
  • Phase 2 — desugar + eager eval + ADTs (data declarations, constructor tagging, pattern matching). Still untyped.
  • Phase 3laziness: thunk-wrap every application arg, force = WHNF, pattern match forces scrutinee. Classic programs (infinite Fibonacci, sieve of Eratosthenes, quicksort, n-queens, expression calculator) green.
  • Phase 4 — Hindley-Milner type inference (Algorithm W, let-polymorphism, type-sig checking)
  • Phase 5 — typeclasses (dictionary passing, Eq/Ord/Show/Num/Functor/Monad/Applicative, deriving)
  • Phase 6 — real IO monad backed by perform/resume, full Prelude, drive corpus to 150+

Within a phase, pick the checkbox with the best tests-per-effort ratio.

Every iteration: implement → test → commit → tick [ ] → Progress log → next.

Ground rules (hard)

  • Scope: only lib/haskell/** and plans/haskell-on-sx.md. Do not edit spec/, hosts/, shared/, other lib/<lang>/ dirs, lib/stdlib.sx, lib/ root, or sx-haskell/. Haskell primitives go in lib/haskell/runtime.sx.
  • NEVER call sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop.
  • Shared-file issues → plan's Blockers with minimal repro.
  • SX thunks (make-thunk, force on use) are already in the trampolining evaluator — reuse. Don't invent your own thunk type.
  • SX files: sx-tree MCP tools ONLY. sx_validate after edits.
  • Worktree: commit locally. Never push. Never touch main.
  • Commit granularity: one feature per commit.
  • Plan file: update Progress log + tick boxes every commit.

Haskell-specific gotchas

  • Layout rule is the hard bit of parsing — you need a lexer-parser feedback loop that inserts virtual {, ;, } based on indentation. Budget proportionally.
  • Every application arg is a thunk — compiling f x y to (f (thunk x) (thunk y)) not (f x y). Pattern-match forces.
  • ADT representation: tagged list, e.g. data Maybe a = Nothing | Just a → constructors are (:Nothing) (0-ary) and (:Just <thunk>) (1-ary). Pattern match on the head symbol.
  • Let-polymorphism (phase 4): generalise at let-binding boundaries only, not at lambda.
  • Typeclass dictionaries (phase 5): each class is a record type; each instance builds the record; method call = project + apply.
  • IO (phase 6): internally World -> (a, World) but in practice backed by perform/resume for real side effects. Desugar do-notation to >>=.
  • Out of scope: GHC extensions. No DataKinds, GADTs, TypeFamilies, TemplateHaskell. Stick to Haskell 98.

General gotchas (all loops)

  • SX do = R7RS iteration. Use begin for multi-expr sequences.
  • cond/when/let clauses evaluate only the last expr.
  • type-of on user fn returns "lambda".
  • Shell heredoc || gets eaten — escape or use case.

Style

  • No comments in .sx unless non-obvious.
  • No new planning docs — update plans/haskell-on-sx.md inline.
  • Short, factual commit messages (haskell: layout rule + first parse (+10)).
  • One feature per iteration. Commit. Log. Next.

Go. Read the plan; (first run only) peek at sx-haskell/ and report; find first [ ]; implement.