# haskell-on-sx loop agent (single agent, queue-driven) Role: iterates `plans/haskell-completeness.md` forever. Mini-Haskell 98 with real laziness (SX thunks are first-class). Phases 1–6 are complete; this loop works Phases 7–16. ``` 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-loops/haskell/plans/haskell-completeness.md`. Isolated worktree, forever, one commit per feature. Push to `origin/loops/haskell` after every commit. ## Restart baseline — check before iterating 1. Read `plans/haskell-completeness.md` — roadmap + Progress log. 2. `ls lib/haskell/` — orient on current state. 3. Run `bash lib/haskell/test.sh`. All 775 tests must be green before new work. 4. Check `lib/haskell/scoreboard.md` — baseline is 156/156 (18 programs). ## The queue Phase order per `plans/haskell-completeness.md`: - **Phase 7** — String = [Char] via O(1) string-view dicts. No OCaml changes. Read the "String-view design" section below before touching anything. - **Phase 8** — `show` for arbitrary types; `deriving Show` generates proper instances; `print x = putStrLn (show x)`. - **Phase 9** — `error` / `undefined`; partial functions raise; top-level runner catches and a new `hk-test-error` helper checks error messages. - **Phase 10** — Numeric tower: `fromIntegral`, Float/Double literals, `sqrt`/`floor`/`ceiling`/`round`/`truncate`, `Fractional`/`Floating` stubs. - **Phase 11** — `Data.Map` — weight-balanced BST in pure SX in `map.sx`. - **Phase 12** — `Data.Set` — BST in pure SX in `set.sx`. - **Phase 13** — `where` in typeclass instances + default methods. - **Phase 14** — Record syntax: `data Foo = Foo { bar :: Int }`, accessors, update `r { field = v }`, record patterns. - **Phase 15** — `IORef` — mutable cells via existing `perform`/`resume` IO. - **Phase 16** — Exception handling: `catch`, `try`, `throwIO`, `evaluate`. Within a phase, pick the checkbox with the best tests-per-effort ratio. Every iteration: implement → test → commit → tick `[ ]` → Progress log → push. ## String-view design (Phase 7 — read before touching strings) A string view is a pure-SX dict `{:hk-str buf :hk-off n}`. Native SX strings also satisfy `hk-str?` (offset = 0 implicitly). No OCaml changes needed. - `hk-str?` covers both native strings and view dicts. - `hk-str-head v` returns the character at offset `n` as an **integer** (ord value). Char = integer throughout. - `hk-str-tail v` returns a new view dict with offset `n+1`; **O(1)**. - `hk-str-null? v` is true when offset ≥ string length. - In `match.sx`, the `":"` cons-pattern branch checks `hk-str?` on the scrutinee **before** the normal tagged-list path. On a string: head = char-int, tail = shifted view (or `(list "[]")` if exhausted). - `chr n` converts an integer back to a single-character SX string for display and for `++`. - `++` between two strings concatenates natively via `str`; no cons-spine built. - The natural hazard: any code that checks `(list? v)` or `(= (first v) ":")` on a value must be audited — string views are dicts, not lists. Check `hk-str?` first in every dispatch chain. ## Conformance test programs For each phase's conformance programs: 1. **WebFetch the source** from one of: - 99 Haskell Problems: https://wiki.haskell.org/H-99:_Ninety-Nine_Haskell_Problems - Rosetta Code Haskell: https://rosettacode.org/wiki/Category:Haskell - Self-contained snippets from Real World Haskell / Learn You a Haskell 2. **Adapt minimally** — no GHC extensions, no external packages beyond `Data.Map`/`Data.Set`/`Data.IORef` (once those phases are done). 3. **Cite the source** as a comment at the top of the `.sx` test file. 4. Add the program name (without `.sx`) to `PROGRAMS` in `lib/haskell/conformance.sh`. 5. Run `bash lib/haskell/conformance.sh` and verify green before committing. Target: scoreboard grows from 156 → 300+ as phases complete. ## Ground rules (hard) - **Scope:** only `lib/haskell/**` and `plans/haskell-completeness.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib//` dirs, `lib/stdlib.sx`, `lib/` root. - **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken → Blockers entry in the plan, stop. - **Shared-file issues** → plan's Blockers section with minimal repro. - **SX thunks** (`make-thunk`, force on use) already in the trampolining evaluator — reuse. String views are SX dicts, not thunks. - **SX files:** `sx-tree` MCP tools ONLY (`sx_read_subtree`, `sx_find_all`, `sx_replace_node`, `sx_insert_child`, `sx_insert_near`, `sx_replace_by_pattern`, `sx_rename_symbol`, `sx_validate`, `sx_write_file`). `sx_validate` after every edit. Never `Edit`/`Read`/`Write` on `.sx` files. - **Shell, Markdown, JSON:** edit with normal tools. - **Worktree:** commit then push to `origin/loops/haskell`. Never touch `main`. - **Commit granularity:** one feature per commit. - **Plan file:** update Progress log + tick boxes every commit. - **Tests:** `bash lib/haskell/test.sh` must stay green. Never regress existing 775 tests. After new programs, run `bash lib/haskell/conformance.sh`. ## Haskell-specific gotchas - **String views are dicts** — `(list? v)` returns false for a string view. Audit every value-dispatch chain in `match.sx` and `eval.sx` for this. - **Char = integer** — `'a'` parses to int 97. `chr 97 = "a"` (1-char string). Do not represent Char as a 1-char SX string internally. - **`deriving Show`** (Phase 8): nested constructor args need parens if their show string contains a space. Rule: `if string-contains (show arg) " " then "(" ++ show arg ++ ")" else show arg`. - **`error` tag** (Phase 9): use `(raise (list "hk-error" msg))`. The top-level `hk-run-io` guard must catch this tag; do not let `hk-error` leak as an uncaught SX exception into the test runner's output. - **`Data.Map` module resolution** (Phase 11): qualified imports `import qualified Data.Map as Map` need the eval import handler to resolve the dotted module name to the `map.sx` namespace dict. Check `hk-bind-decls!` import arm. - **Record update field index** (Phase 14): `r { field = v }` needs the field → positional-index mapping at runtime. Store it in `hk-constructors` when registering `:con-rec`. - **IORef mutation** (Phase 15): `dict-set!` is the SX in-place mutator. The `IORef` dict is heap-allocated and passed by reference — mutation is safe. - **Every application arg is a thunk** — `f x y` → `(f (thunk x) (thunk y))`. Pattern-match forces before matching. Builtins force their args. - **ADT representation:** `("Just" thunk)`, `("Nothing")`, `(":" h t)`, `("[]")`. - **Let-polymorphism:** generalise at let-binding boundaries only, not lambda. - **Typeclass dictionaries:** class = record; instance = record value; method call = project + apply. Defaults stored under `"__default__ClassName_method"`, used as fallback when the instance dict lacks the key. - **Out of scope:** GHC extensions. No `DataKinds`, `GADTs`, `TypeFamilies`, `TemplateHaskell`. Haskell 98 only. ## General gotchas (all loops) - SX `do` = R7RS iteration. Use `begin` for multi-expression sequences. - `cond`/`when`/`let` clauses evaluate only the last expression. - `type-of` on user fn returns `"lambda"`. - Shell heredoc `||` gets eaten by bash — escape or use `case`. - `keys` on an SX dict returns keys in implementation-defined order. ## Style - No comments in `.sx` unless non-obvious. - No new planning docs — update `plans/haskell-completeness.md` inline. - Short, factual commit messages (`haskell: string-view O(1) head/tail (+15)`). - One feature per iteration. Commit. Log. Next. Go. Read `plans/haskell-completeness.md`; find the first `[ ]`; implement.