parse-reader: splice multi-line def := continuations (eigentrust pitfall #15)#12
Conversation
2ebdb15 to
5331e70
Compare
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to prologos::core::pvec, mirroring the existing nth-int / length-int / take-int / drop-int quartet on List. These let an algorithm that already carries Int counters (e.g. for an int-le budget termination check) index into a PVec without maintaining a parallel Nat counter. Implementation routes through pvec-to-list and the existing List Int helpers (and back via pvec-from-list for take/drop), avoiding a freshly-introduced Int->Nat conversion primitive. An earlier draft defined a private int-to-nat-clamp helper and called pvec-slice directly, but pvec-slice's whnf rule needs nat-value to succeed on its lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far enough inside the slice's whnf to satisfy nat-value, leaving the slice stuck. Routing through List sidesteps this entirely and matches the List versions' semantics exactly: negative indices are out-of-range, out-of-bounds nth returns none, take/drop clamp to [0, length]. Cost: O(n) for nth-int/take-int/drop-int (matching the List counterparts). pvec-length-int is O(log32 n). Did NOT add a top-level Int -> Nat helper. The pitfall noted that from-int : Int -> Nat does not exist, but `from-int` is already a parser keyword (Int -> Rat), and the List helpers themselves avoid the conversion via pure recursion — so a public Int -> Nat would just be a foot-gun that doesn't unblock anything the new helpers don't already address. Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2, nth at 0/1/last/negative/out-of-bounds/empty, take and drop at 1/0/negative/larger-than-length, and a take+drop length round-trip. A standalone smoke test (run separately) exercises all 16 cases via process-string + pvec-to-list/pvec-length-int and confirms PASS on both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1. The test file uses the standard test-support.rkt + rackunit pattern; the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents running it via `raco test` here, but it is ready for the standard test runner. https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
5331e70 to
1f67a47
Compare
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to prologos::core::pvec, mirroring the existing nth-int / length-int / take-int / drop-int quartet on List. These let an algorithm that already carries Int counters (e.g. for an int-le budget termination check) index into a PVec without maintaining a parallel Nat counter. Implementation routes through pvec-to-list and the existing List Int helpers (and back via pvec-from-list for take/drop), avoiding a freshly-introduced Int->Nat conversion primitive. An earlier draft defined a private int-to-nat-clamp helper and called pvec-slice directly, but pvec-slice's whnf rule needs nat-value to succeed on its lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far enough inside the slice's whnf to satisfy nat-value, leaving the slice stuck. Routing through List sidesteps this entirely and matches the List versions' semantics exactly: negative indices are out-of-range, out-of-bounds nth returns none, take/drop clamp to [0, length]. Cost: O(n) for nth-int/take-int/drop-int (matching the List counterparts). pvec-length-int is O(log32 n). Did NOT add a top-level Int -> Nat helper. The pitfall noted that from-int : Int -> Nat does not exist, but `from-int` is already a parser keyword (Int -> Rat), and the List helpers themselves avoid the conversion via pure recursion — so a public Int -> Nat would just be a foot-gun that doesn't unblock anything the new helpers don't already address. Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2, nth at 0/1/last/negative/out-of-bounds/empty, take and drop at 1/0/negative/larger-than-length, and a take+drop length round-trip. A standalone smoke test (run separately) exercises all 16 cases via process-string + pvec-to-list/pvec-length-int and confirms PASS on both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1. The test file uses the standard test-support.rkt + rackunit pattern; the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents running it via `raco test` here, but it is ready for the standard test runner. https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to prologos::core::pvec, mirroring the existing nth-int / length-int / take-int / drop-int quartet on List. These let an algorithm that already carries Int counters (e.g. for an int-le budget termination check) index into a PVec without maintaining a parallel Nat counter. Implementation routes through pvec-to-list and the existing List Int helpers (and back via pvec-from-list for take/drop), avoiding a freshly-introduced Int->Nat conversion primitive. An earlier draft defined a private int-to-nat-clamp helper and called pvec-slice directly, but pvec-slice's whnf rule needs nat-value to succeed on its lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far enough inside the slice's whnf to satisfy nat-value, leaving the slice stuck. Routing through List sidesteps this entirely and matches the List versions' semantics exactly: negative indices are out-of-range, out-of-bounds nth returns none, take/drop clamp to [0, length]. Cost: O(n) for nth-int/take-int/drop-int (matching the List counterparts). pvec-length-int is O(log32 n). Did NOT add a top-level Int -> Nat helper. The pitfall noted that from-int : Int -> Nat does not exist, but `from-int` is already a parser keyword (Int -> Rat), and the List helpers themselves avoid the conversion via pure recursion — so a public Int -> Nat would just be a foot-gun that doesn't unblock anything the new helpers don't already address. Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2, nth at 0/1/last/negative/out-of-bounds/empty, take and drop at 1/0/negative/larger-than-length, and a take+drop length round-trip. A standalone smoke test (run separately) exercises all 16 cases via process-string + pvec-to-list/pvec-length-int and confirms PASS on both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1. The test file uses the standard test-support.rkt + rackunit pattern; the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents running it via `raco test` here, but it is ready for the standard test runner. https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
hierophantos
left a comment
There was a problem hiding this comment.
LGTM. Surgical scope mirrors #4 (preparser splice based on first-token check), with a deliberately narrower rule: only :=-headed continuations splice; bare bodies stay wrapped — preserving def c\n + 1 2 → (def c (+ 1 2)) semantics for the existing bare-body idiom. End-to-end test coverage (using run-ns-ws-last) pins the silent-suppression case, which is the worst-quality bug shape (no error, just no work — was reporting reduce_ms = 0 in benchmarks).
Audit-passed against our codebase: 354 single-line def := body exist, all unaffected; zero multi-line def usage today, so the change is purely additive.
Now that #4 has landed (3d7ccb19), this PR is showing CONFLICTING — per your own note in the PR body, needs a rebase to drop the duplicate spec-form-node? / flatten-with-boundaries/spec / continuation-starts-with-keyword? (now on main) and add the def-form-node? branch on top of the existing spec-form-node? cond clause.
Approving — ready to merge once the rebase lands.
1f67a47 to
b51900d
Compare
b51900d to
d1c92a5
Compare
Wires the OCapN port to a real Racket toolchain and fixes the issues
the test run surfaced.
Library fixes
- vat.prologos: rename `spawn` -> `vat-spawn` (collision with the
reserved surface form recognised in macros.rkt:`'spawn`),
`spawn-actor` -> `vat-spawn-actor`. Same in core.prologos and the
acceptance file.
- vat.prologos: drop the `Sigma Vat Nat` return shape for spawn /
fresh-promise / send. Replace with a named `Allocated` struct +
`alloc-vat` / `alloc-id` accessors. The Sigma form ran into
"could not infer" elaborator errors when the body destructured
via `match | pair a b -> ...` and then re-constructed a Sigma;
`[fst p]` / `[snd p]` reused on the same `p` tripped QTT
multiplicity. The named struct sidesteps both.
- vat.prologos: reorder `resolve-promise` / `break-promise` BEFORE
`apply-effect` (forward-reference rule — module elaboration is
single-pass top-to-bottom). Also reorder `step-after-act` before
`deliver-msg` and `list-length-helper` before `queue-length`.
- vat.prologos: drop the queued-pipeline-flush in resolve-promise /
break-promise. PromiseState's queue is `List SyrupValue` (wire
repr); the vat queue is `List VatMsg` (decoded); flushing across
the boundary would need re-encoding. Phase 1.
Test-fixture fix (load-bearing)
- All 8 OCapN test files were updated to capture and restore
`current-ctor-registry` and `current-type-meta` across the setup-
-> run boundary. The standard fixture pattern from
`test-hashable-01.rkt` does NOT preserve these — fine for tests
that only declare traits, but breaks once a preamble's imports
declare new `data` types (every `data` in our 8 modules). Without
it, the reducer sees a stale ctor-registry and refuses to fire
pattern arms over user constructors; results print as un-reduced
`[reduce ... | vat x y z a -> x] : Nat` strings.
Documented as goblin-pitfall #12; the canonical fixture in
test-support.rkt should grow this for every future test.
Compat fence
- driver.rkt: guard
`(current-parallel-executor (make-parallel-thread-fire-all))` with
a feature-detection try/catch on `thread #:pool 'own`. Racket 9
ships parallel threads; Racket 8 does not. Fence preserves the
Racket-9 fast path and falls back to sequential firing on 8.
Acceptance
- examples/2026-04-27-ocapn-acceptance.prologos updated to match
the new vat-spawn/Allocated API and verified to run clean via
process-file.
Pitfalls catalogue (docs/tracking/2026-04-27_GOBLIN_PITFALLS.md)
- #0 (sandbox/no-Racket): closed.
- +#11 — Racket-8 vs Racket-9 `thread #:pool` compat
- +#12 — test fixture loses ctor-registry/type-meta across calls
[highest-impact; canonical fixture pattern needs update]
- +#13 — `spawn` is a reserved surface keyword; collides silently
- +#14 — `match | pair a b ->` on Sigma + Sigma reconstruction =>
"could not infer"
- +#15 — QTT multiplicity on `[fst p]`/`[snd p]` reused thrice
- +#16 — single-pass module elaboration: forward references error
- +#17 — promise-queue (Syrup) vs vat-queue (VatMsg) type clash
on flush — design pitfall, scope cut
Test results
refr 6/6 syrup 22/22 promise 16/16 message 19/19
behavior 13/13 vat 21/21 pipeline 5/5 captp 7/7
e2e 8/8 total 117/117 PASS
Per user direction: - Replace the body of every DELETED entry with a single-sentence explanation. Numbers reserved per prior instruction. - Delete #15 (QTT multiplicity on fst/snd thrice). I re-tested with a real Racket — `pair [snd p] [fst p]` then a third use of `fst p` works fine; no multiplicity error. The failure I had conflated this with was actually #14's "match-and-reconstruct Sigma" issue. Result: pitfalls doc shrinks from 765 to 534 lines. Remaining real claims: #1, #4, #5, #11, #12, #13, #14, #16, #17, #18, #19, #20 (the user has reviewed only #0-10 so far; #11-20 still pending their review).
…alls #15) A two-line def with type annotation — def c : [List Int] := '[1 2 3] — silently suppressed evaluation: PHASE-TIMINGS reported reduce_ms=0 and the bound name elaborated but never reduced. The same def collapsed to one line worked normally. The silent-no-work shape was particularly bad for benchmarks (reported zero reduce time regardless of workload). Add a def-form-node? branch to tree-node->stx-elements and a flatten-with-boundaries/def variant that splices ONLY continuations whose first token is :=. All other continuations (bare body, bracketed application chain, etc.) stay wrapped, preserving today's def c + 1 2 → (def c (+ 1 2)) semantics for the bare-body idiom. Narrower than the spec splice rule (#4 / #6) on purpose: spec needs to expose -> from anywhere in a multi-line type signature, def only needs to expose := which is always the head of a natural line break. Test coverage in test-def-multiline-ws.rkt covers: - Datum equivalence between one-line and two-line shapes (12 cases). - Bare-body wrapping is preserved. - End-to-end via run-ns-ws-last (silent-suppression regression pin). - Multi-line BODY after :=, := alone on a line, and definitions spanning lines as bracketed groups. Three-level WS validation: - Level 1 (sexp): the test file uses run-ns-last where applicable. - Level 2 (WS string): ws-read / run-ns-ws-last cover the dispatch and elaboration paths. - Level 3 (WS file): process-file on a .prologos with the two-line form reports reduce_ms=5 / reduce_steps=39, matching the one-line shape exactly (was reduce_ms=0 / reduce_steps=0 before). https://claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
d1c92a5 to
274cd2a
Compare
Adds pvec-nth-int / pvec-length-int / pvec-take-int / pvec-drop-int to prologos::core::pvec, mirroring the existing nth-int / length-int / take-int / drop-int quartet on List. These let an algorithm that already carries Int counters (e.g. for an int-le budget termination check) index into a PVec without maintaining a parallel Nat counter. Implementation routes through pvec-to-list and the existing List Int helpers (and back via pvec-from-list for take/drop), avoiding a freshly-introduced Int->Nat conversion primitive. An earlier draft defined a private int-to-nat-clamp helper and called pvec-slice directly, but pvec-slice's whnf rule needs nat-value to succeed on its lo/hi args — and a defn-defined int-to-nat-clamp does not reduce far enough inside the slice's whnf to satisfy nat-value, leaving the slice stuck. Routing through List sidesteps this entirely and matches the List versions' semantics exactly: negative indices are out-of-range, out-of-bounds nth returns none, take/drop clamp to [0, length]. Cost: O(n) for nth-int/take-int/drop-int (matching the List counterparts). pvec-length-int is O(log32 n). Did NOT add a top-level Int -> Nat helper. The pitfall noted that from-int : Int -> Nat does not exist, but `from-int` is already a parser keyword (Int -> Rat), and the List helpers themselves avoid the conversion via pure recursion — so a public Int -> Nat would just be a foot-gun that doesn't unblock anything the new helpers don't already address. Tests: tests/test-pvec-int-helpers.rkt covers length on empty/1/2, nth at 0/1/last/negative/out-of-bounds/empty, take and drop at 1/0/negative/larger-than-length, and a take+drop length round-trip. A standalone smoke test (run separately) exercises all 16 cases via process-string + pvec-to-list/pvec-length-int and confirms PASS on both Racket 8.10 (with current-parallel-executor #f) and Racket 9.1. The test file uses the standard test-support.rkt + rackunit pattern; the pre-existing (thread #:pool 'own) blocker on Racket 8.10 prevents running it via `raco test` here, but it is ready for the standard test runner. https: //claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x Co-authored-by: kumavis <1474978+kumavis@users.noreply.github.com>
Wires the OCapN port to a real Racket toolchain and fixes the issues
the test run surfaced.
Library fixes
- vat.prologos: rename `spawn` -> `vat-spawn` (collision with the
reserved surface form recognised in macros.rkt:`'spawn`),
`spawn-actor` -> `vat-spawn-actor`. Same in core.prologos and the
acceptance file.
- vat.prologos: drop the `Sigma Vat Nat` return shape for spawn /
fresh-promise / send. Replace with a named `Allocated` struct +
`alloc-vat` / `alloc-id` accessors. The Sigma form ran into
"could not infer" elaborator errors when the body destructured
via `match | pair a b -> ...` and then re-constructed a Sigma;
`[fst p]` / `[snd p]` reused on the same `p` tripped QTT
multiplicity. The named struct sidesteps both.
- vat.prologos: reorder `resolve-promise` / `break-promise` BEFORE
`apply-effect` (forward-reference rule — module elaboration is
single-pass top-to-bottom). Also reorder `step-after-act` before
`deliver-msg` and `list-length-helper` before `queue-length`.
- vat.prologos: drop the queued-pipeline-flush in resolve-promise /
break-promise. PromiseState's queue is `List SyrupValue` (wire
repr); the vat queue is `List VatMsg` (decoded); flushing across
the boundary would need re-encoding. Phase 1.
Test-fixture fix (load-bearing)
- All 8 OCapN test files were updated to capture and restore
`current-ctor-registry` and `current-type-meta` across the setup-
-> run boundary. The standard fixture pattern from
`test-hashable-01.rkt` does NOT preserve these — fine for tests
that only declare traits, but breaks once a preamble's imports
declare new `data` types (every `data` in our 8 modules). Without
it, the reducer sees a stale ctor-registry and refuses to fire
pattern arms over user constructors; results print as un-reduced
`[reduce ... | vat x y z a -> x] : Nat` strings.
Documented as goblin-pitfall #12; the canonical fixture in
test-support.rkt should grow this for every future test.
Compat fence
- driver.rkt: guard
`(current-parallel-executor (make-parallel-thread-fire-all))` with
a feature-detection try/catch on `thread #:pool 'own`. Racket 9
ships parallel threads; Racket 8 does not. Fence preserves the
Racket-9 fast path and falls back to sequential firing on 8.
Acceptance
- examples/2026-04-27-ocapn-acceptance.prologos updated to match
the new vat-spawn/Allocated API and verified to run clean via
process-file.
Pitfalls catalogue (docs/tracking/2026-04-27_GOBLIN_PITFALLS.md)
- #0 (sandbox/no-Racket): closed.
- +#11 — Racket-8 vs Racket-9 `thread #:pool` compat
- +#12 — test fixture loses ctor-registry/type-meta across calls
[highest-impact; canonical fixture pattern needs update]
- +#13 — `spawn` is a reserved surface keyword; collides silently
- +#14 — `match | pair a b ->` on Sigma + Sigma reconstruction =>
"could not infer"
- +#15 — QTT multiplicity on `[fst p]`/`[snd p]` reused thrice
- +#16 — single-pass module elaboration: forward references error
- +#17 — promise-queue (Syrup) vs vat-queue (VatMsg) type clash
on flush — design pitfall, scope cut
Test results
refr 6/6 syrup 22/22 promise 16/16 message 19/19
behavior 13/13 vat 21/21 pipeline 5/5 captp 7/7
e2e 8/8 total 117/117 PASS
Per user direction: - Replace the body of every DELETED entry with a single-sentence explanation. Numbers reserved per prior instruction. - Delete #15 (QTT multiplicity on fst/snd thrice). I re-tested with a real Racket — `pair [snd p] [fst p]` then a third use of `fst p` works fine; no multiplicity error. The failure I had conflated this with was actually #14's "match-and-reconstruct Sigma" issue. Result: pitfalls doc shrinks from 765 to 534 lines. Remaining real claims: #1, #4, #5, #11, #12, #13, #14, #16, #17, #18, #19, #20 (the user has reviewed only #0-10 so far; #11-20 still pending their review).
…2-pv12 pvec: add Int-indexed helpers (eigentrust pitfall #12)
|
Post-rebase verified clean: dispatch in |
Fixes pitfall #15 from
docs/tracking/2026-04-23_eigentrust_pitfalls.md.Summary
silently suppressed evaluation of downstream top-level expressions. The reducer never fired,
PHASE-TIMINGSreportedreduce_ms = 0, and the computed value was never printed. The samedefcollapsed to one line worked normally (reduce_ms = 38_855for a 3-iter benchmark).Load-bearing for benchmark validity — silent suppression was the worst of both worlds (no error, but no work).
Reproducer (before/after)
def)d)Unbound variable: $list-literalUnbound variable: d(reduce_ms=0)d : [List Rat] defined'[1/2 1/2 1/2] : [List Rat]Identical to the one-line form.
Fix
WS reader splices the
:= BODYcontinuation into the def's parent token stream. Narrower than the spec splice rule from PR #4: only continuations whose first token is:=are spliced. Bare bodies,+ 1 2applications, bracketed groups stay wrapped, preserving today'sdef c\n + 1 2→(def c (+ 1 2))semantics.Implementation: added
def-form-node?,flatten-with-boundaries/def,continuation-starts-with-assign?toparse-reader.rkt, dispatched intree-node->stx-elements.Coexistence note with PR #4 (pitfall #6)
PR #4 (pitfall #6) is not yet merged to main, but uses the same dispatch site in
tree-node->stx-elements. This PR introduces theflatten-with-boundaries/spectriple from PR #4's commit6c42aa9alongside the new def helpers because both fixes share the dispatch site. Whichever lands second will need a small dedup offlatten-with-boundaries/spec,spec-form-node?, andcontinuation-starts-with-keyword?.Test plan
tests/test-def-multiline-ws.rkt— 12 cases: datum-level equivalence between one-line and two-line forms, end-to-end evaluation regression,:=on its own line, multi-line BODYprocess-ws-01,parse-reader,let-arrow-syntax,spec*,defmacro,cond-01,bound-args-01,int-patterns-01,first-rest-01,hashable-01) all passlattices.prologos,foreign.prologos,2026-04-02-ppn-track3.prologosbenchmarks/micro/info.rkt)Commits
e7dbc0f— primary fix inparse-reader.rkt+ new test file2ebdb15— CI fix (skip stale bench file)https://claude.ai/code/session_01MbncYJnrvjzhbVWw4xGi5x
Generated by Claude Code