String=[Char] via pure-SX views, show, error, numeric tower, Data.Map, Data.Set, records, IORef, exceptions. Briefing updated to point at new plan; old phases 1-6 plan untouched. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.7 KiB
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
- Read
plans/haskell-completeness.md— roadmap + Progress log. ls lib/haskell/— orient on current state.- Run
bash lib/haskell/test.sh. All 775 tests must be green before new work. - 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 —
showfor arbitrary types;deriving Showgenerates proper instances;print x = putStrLn (show x). - Phase 9 —
error/undefined; partial functions raise; top-level runner catches and a newhk-test-errorhelper checks error messages. - Phase 10 — Numeric tower:
fromIntegral, Float/Double literals,sqrt/floor/ceiling/round/truncate,Fractional/Floatingstubs. - Phase 11 —
Data.Map— weight-balanced BST in pure SX inmap.sx. - Phase 12 —
Data.Set— BST in pure SX inset.sx. - Phase 13 —
wherein typeclass instances + default methods. - Phase 14 — Record syntax:
data Foo = Foo { bar :: Int }, accessors, updater { field = v }, record patterns. - Phase 15 —
IORef— mutable cells via existingperform/resumeIO. - 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 vreturns the character at offsetnas an integer (ord value). Char = integer throughout.hk-str-tail vreturns a new view dict with offsetn+1; O(1).hk-str-null? vis true when offset ≥ string length.- In
match.sx, the":"cons-pattern branch checkshk-str?on the scrutinee before the normal tagged-list path. On a string: head = char-int, tail = shifted view (or(list "[]")if exhausted). chr nconverts an integer back to a single-character SX string for display and for++.++between two strings concatenates natively viastr; 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. Checkhk-str?first in every dispatch chain.
Conformance test programs
For each phase's conformance programs:
- 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
- Adapt minimally — no GHC extensions, no external packages beyond
Data.Map/Data.Set/Data.IORef(once those phases are done). - Cite the source as a comment at the top of the
.sxtest file. - Add the program name (without
.sx) toPROGRAMSinlib/haskell/conformance.sh. - Run
bash lib/haskell/conformance.shand verify green before committing.
Target: scoreboard grows from 156 → 300+ as phases complete.
Ground rules (hard)
- Scope: only
lib/haskell/**andplans/haskell-completeness.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/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-treeMCP 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_validateafter every edit. NeverEdit/Read/Writeon.sxfiles. - Shell, Markdown, JSON: edit with normal tools.
- Worktree: commit then push to
origin/loops/haskell. Never touchmain. - Commit granularity: one feature per commit.
- Plan file: update Progress log + tick boxes every commit.
- Tests:
bash lib/haskell/test.shmust stay green. Never regress existing 775 tests. After new programs, runbash lib/haskell/conformance.sh.
Haskell-specific gotchas
- String views are dicts —
(list? v)returns false for a string view. Audit every value-dispatch chain inmatch.sxandeval.sxfor 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.errortag (Phase 9): use(raise (list "hk-error" msg)). The top-levelhk-run-ioguard must catch this tag; do not lethk-errorleak as an uncaught SX exception into the test runner's output.Data.Mapmodule resolution (Phase 11): qualified importsimport qualified Data.Map as Mapneed the eval import handler to resolve the dotted module name to themap.sxnamespace dict. Checkhk-bind-decls!import arm.- Record update field index (Phase 14):
r { field = v }needs the field → positional-index mapping at runtime. Store it inhk-constructorswhen registering:con-rec. - IORef mutation (Phase 15):
dict-set!is the SX in-place mutator. TheIORefdict 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. Usebeginfor multi-expression sequences. cond/when/letclauses evaluate only the last expression.type-ofon user fn returns"lambda".- Shell heredoc
||gets eaten by bash — escape or usecase. keyson an SX dict returns keys in implementation-defined order.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/haskell-completeness.mdinline. - 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.