plans: haskell-completeness phases 7-16 + updated loop briefing
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
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>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# 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.
|
||||
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
|
||||
@@ -11,66 +13,141 @@ 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. Push to `origin/loops/haskell` after every commit.
|
||||
|
||||
**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.
|
||||
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-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.
|
||||
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-on-sx.md`:
|
||||
Phase order per `plans/haskell-completeness.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 3** — **laziness**: 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+
|
||||
- **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 → next.
|
||||
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-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, then push to `origin/loops/haskell`. Never touch `main`.
|
||||
- **Scope:** only `lib/haskell/**` and `plans/haskell-completeness.md`. Do
|
||||
**not** edit `spec/`, `hosts/`, `shared/`, other `lib/<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-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
|
||||
|
||||
- **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.
|
||||
- **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-expr sequences.
|
||||
- `cond`/`when`/`let` clauses evaluate only the last expr.
|
||||
- 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 — escape or use `case`.
|
||||
- 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-on-sx.md` inline.
|
||||
- Short, factual commit messages (`haskell: layout rule + first parse (+10)`).
|
||||
- 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 the plan; (first run only) peek at sx-haskell/ and report; find first `[ ]`; implement.
|
||||
Go. Read `plans/haskell-completeness.md`; find the first `[ ]`; implement.
|
||||
|
||||
Reference in New Issue
Block a user