The self-hosted eval's bootstrap still has its own apply (for use wit… #70
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
seq-lisp-2-backport
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/seq-lisp!70
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "self-hosting-dedupe"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
…hin seval-string/sload), and the host keeps native apply for the dispatch-only
builtin bridging. That's two apply implementations — but for a fundamentally different reason than the others: it's the one function that needs to know
about the host's internal dispatch mechanism.
Ready for your PR whenever you want to cut one, or we can keep going.
Code Review
Nice step forward on the self-hosting dedupe work. The direction is right — removing the native Seq implementations of
map/filter/fold/notand routing through the bootstrap closures is exactly what the design doc calls for. A few issues worth addressing before merging:🐛
eval-printstill usesenv-empty(likely a bug)src/eval.seq:5361This word was not updated to use
bootstrap-env. Any caller that goes througheval-print(test harness, perhaps?) won't have access tomap,filter,fold, ornot. Worth checking whether this path is exercised by tests.⚠️
bootstrap-envis not actually cachedsrc/eval.seq:5409-5413The comment says "cached at first call site" but there is no caching — every call parses and evaluates the bootstrap source from scratch. It's called 5 times in
repl.seqalone (lines 131, 285, 312, 369, 384) plus theeval.seqinternal calls. In the REPL this means the bootstrap is re-evaluated on every file load and every piped-stdin invocation.If caching was intentional (or even just desirable for performance), the
bootstrap-envword needs to store the result somewhere global. If the current repeated-evaluation is acceptable, the comment is misleading and should be removed.🔧
lib/bootstrap.slispand the embedded string can driftThe readable file
lib/bootstrap.slispis never actually loaded by the interpreter — the embedded string inbootstrap-sourceis what's evaluated. They are currently in sync but there's nothing preventing them from diverging. This is exactly the open question noted indocs/design/self-hosting-phase2.md:Two options:
bootstrap-envreadslib/bootstrap.slispat startup. Eliminates the duplicate. Adds a filesystem dependency at startup.lib/bootstrap.slisp— If the file is only documentation/reference and isn't going to be loaded, state that clearly in a comment and consider putting it indocs/rather thanlib/.As-is,
lib/bootstrap.slisphas a misleading header comment: "These functions are the single implementation — there are no native Seq versions. They are loaded at host startup..." — they are not loaded from this file. That comment will confuse future contributors.📋 Step ordering vs. design doc
docs/design/self-hosting-phase2.mdspecifies:This PR implements Step 2 before Step 1. The
map/filter/foldimplementations in the bootstrap are all straightforwardly recursive. On a large list (e.g.(map f (range 100000))) they will stack overflow — the same regression the design doc flags. Is this intentional (i.e., TCO is tracked separately)? If so, noting it explicitly in the PR description or a code comment would help.Minor: bootstrap error handling continues on failure
src/eval.seq:5396-5404If a bootstrap definition fails to parse or evaluate, the loop prints a message and continues. This means you can end up with an environment where
mapornotis silently missing. For a bootstrap where all definitions are statically known, it may be safer to halt (or at minimum exit non-zero) so the failure is visible during development.Summary
eval-printnot updated tobootstrap-envbootstrap-envcomment claims caching, no caching existslib/bootstrap.slispcomment says "loaded at startup" but it isn'ttest comment - ignore
DISREGARD - this is a test comment that will be deleted
Code Review
Note: A prior automated review comment on this PR contained two factual errors —
eval-printwas updated (the diff confirms it), and thebootstrap-envcomment correctly documents the re-evaluation behaviour. This review supersedes that one.What this PR does well
map/filter/fold— the dispatch chain is simpler and thethencount drops correctly from 15 to 12.bootstrap-eval-loopcallsos.exiton a fatal bootstrap error rather than silently continuing. That's the right call for a startup-time invariant.bootstrap-envis threaded into all external entry points consistently (repl-loop,run-file,eval-all-print,eval-all-quiet, piped stdin).applystaying native is clear and accurate.Issues
1. Step ordering violates the design doc constraint (Medium)
docs/design/self-hosting-phase2.mdis explicit:The doc specifies Step 1 (TCO via trampoline) before Step 2 (deduplication). This PR implements Step 2 first. The bootstrap implementations of
map,filter, andfoldare all naively recursive. On a long list they will stack overflow — the exact regression the constraint was written to prevent.Is this intentional (TCO tracked separately, regression accepted temporarily)? If so, a comment in
lib/bootstrap.slispand a note in the PR description would help future reviewers understand the tradeoff.2. Design doc specifies renaming host `apply` to `__host-apply__` (Medium)
self-hosting-phase2.md, Step 2:This PR keeps the host
applyin the dispatch chain under the user-facing nameapply, and the bootstrap does not define a self-hostedapply. That's a conscious deviation from the spec — but the PR description acknowledges it. Worth a note in the design doc recording this decision, or an update to the spec if the approach has changed.3. `lib/bootstrap.slisp` header comment is misleading (Low)
The file header says the functions "are loaded at host startup" but they are NOT loaded from this file. The embedded string in
eval.seq:bootstrap-sourceis what actually runs. A future contributor will search for afile.slurpcall and not find one.Suggested fix: update the comment to say this file is documentation/reference only, and that the canonical source is the embedded string in
eval.seq.4. Silent drop of non-define results in `bootstrap-eval-loop` (Low)
If a bootstrap expression evaluates to a non-define, non-error value, the result is silently dropped. For a statically known bootstrap this is fine, but a short comment clarifying intent would help: "top-level non-define results are discarded — all bootstrap forms should be defines".
Summary
applynot renamed to__host-apply__per design doclib/bootstrap.slispsays "loaded at startup" but it is notbootstrap-eval-loopsilently dropped without commentThe core mechanism is sound. The biggest risk before merging is clarifying the TCO ordering decision — either document that the regression is accepted, or gate this behind Step 1.
PR Review: Self-hosted bootstrap for map/filter/fold/not
Overall this is a clean, well-motivated change. The architecture is sound — removing native dispatch for the four self-hostable functions and bootstrapping them from embedded SeqLisp source is exactly the right direction. A few issues to address:
Bug: Inaccurate comment in
repl.seqapplyis not in the bootstrap environment — it stays in the native dispatch chain. The comment should readmap, filter, fold, not(noapply). Small thing, but it directly contradicts the design intent documented everywhere else.Performance:
bootstrap-envcalled on everyeval-printeval-printineval.seqnow callsbootstrap-envon every invocation:bootstrap-envtokenizes, parses, and evaluates fourdefineforms each time. For the REPL andrun-filethis is a one-shot startup cost, buteval-printis called per-expression intest_eval.seqand anywhere that uses it as a convenience helper. This will add measurable overhead to the test suite.A simple fix would be to not use
bootstrap-envinsideeval-print— instead, callers that want the bootstrap environment should pass one in. Orbootstrap-envcould be memoized/cached at a higher level. The current architecture where it's re-created per call is the main rough edge here.Maintenance risk:
lib/bootstrap.slispdivergenceThe file header says it's a reference copy and not loaded by the interpreter. This is a known fragile pattern — the embedded string in
eval.seqandlib/bootstrap.slispwill inevitably drift. A few options ranked by robustness:lib/bootstrap.slispthe canonical source and have the build system embed it as a string (ajustrecipe that generates the string literal).lib/bootstrap.slisp(strip comments, normalize whitespace, compare).justfileor a CI step reminding maintainers to sync both.Option 1 eliminates the problem entirely and makes the bootstrap source as readable as the
.slispfile.Dead code after
os.exit— comment clarityThe dead code in
bootstrap-eval-loopafteros.exitis correctly identified in the comment, but it might confuse future readers who wonder if the loop tail is actually reachable:Consider phrasing it as:
# Unreachable — compiler requires consistent stack depth after os.exit. That's the conventional way this pattern is documented elsewhere in the codebase (e.g.# Never reaches here - stack shaping for compilerinrepl.seq).Test coverage
The existing
tests/lisp/functions/higher_order.slispcoversmap/filter/fold/applybehavior, which is good. But there's no test for the bootstrap mechanism itself — specifically:notworks post-bootstrap (it's tested implicitly viaifbut not directly as(not #t)→#f)os.exit 1gracefully (hard to test, but worth noting the gap)A minimal addition to
higher_order.slispor a newbootstrap.slisptest file with(assert-eq (not #t) #f)and(assert-eq (not #f) #t)would close this gap.Minor:
check-candidatefornotGood catch adding
"not" check-candidateto the dispatch validator — this ensures the duplicate-implementation check coversnotnow that it's self-hosted. ✓Summary
repl.seqlistsapplyas part of bootstrap outputbootstrap-envre-evaluated on everyeval-printcalllib/bootstrap.slisp/ embedded string will drift without toolingnotvia bootstrapThe core design is solid. Fix the comment bug and consider the
eval-printperformance issue before merging — the rest can be follow-on work.PR Review: Self-Hosting Phase 2 — map/filter/fold/not Bootstrap
Overall this is clean, well-reasoned work. The self-hosting architecture is sound and the rationale for keeping
applynative is well-documented. A few items worth considering:Issues
1.
eval-printcallsbootstrap-envon every invocationeval-printinsrc/eval.seq:5361re-parses and re-evaluates the entire bootstrap source string every call. In production this is fine (startup only), buteval-printis used for every test case intests/seq/test_eval.seq, so the test suite pays the bootstrap cost on each line. The commit message says "not called in hot loops" but the test harness contradicts that. Not a blocker, but worth considering whethereval-printshould useenv-empty(test assertions do not needmap/filter/fold/not), or whether a cached bootstrap env would be worth adding later.2.
lib/bootstrap.slispwill drift from the embedded stringThe new reference file duplicates the canonical source from
src/eval.seq:5377-5383. The comment warns about this clearly, but there is no automated enforcement. Even a simple CI check (extract the embedded string and diff against the file) would prevent silent drift. A future editor who only reads the.slispfile may be confused when their edits have no effect.3.
eval(the compat wrapper) now silently usesbootstrap-envsrc/eval.seq:680— the word previously documented as "for backward compatibility" now usesbootstrap-envinstead ofenv-empty. This is probably correct, but it is a subtle behavior change for any caller that expected a pristine environment. Worth adding a comment noting the intention ("callers now get map/filter/fold/not in scope").Minor / Nits
bootstrap-eval-loopunreachable code comment: the post-os.exitpath is a good pattern for satisfying the compiler stack-depth checker. The existing comment says "unreachable" but could also note that the stack shape intentionally matches theelsebranch so the compiler sees consistent depth on all paths.notincheck-candidatewith no native dispatch: correct, sincenotis new to the language here. But a future reader might wonder if there is supposed to be a nativenothandler that got lost. A short comment next to thatcheck-candidateentry ("self-hosted in bootstrap, no native dispatch") would help.thencount: 12 is correct after removing 3 branches (map/filter/fold). No issue.What is good
applydecision is well-reasoned and documented in both the design doc and the codeos.exit 1on bootstrap failure is the right call — a silent partial bootstrap would cause mysterious downstream errorsbootstrap-eval-loopsafe for long liststests/lisp/functions/higher_order.slispexercises all four self-hosted functions plusapplyVerdict: Ready to merge with the nits above as optional follow-ups. The
lib/bootstrap.slispdrift risk is the one item worth revisiting before the file gets stale — perhaps ajust check-bootstrap-synctarget in the justfile.