WalkLang Explicit Systems Track
Status: implemented.
Date opened: 2026-05-25.
This document is the persistent tracker for one explicit-language implementation slice. It covers four related improvements inspired by Odin-style language discipline:
1. Scope cleanup with defer:. 2. Result values instead of exception-like control flow. 3. Package collections as explicit import roots. 4. First-class build modes.
This is one implementation track, not multiple phases. Implement all four items in one release slice, or stop and record the blocker here before changing the scope.
Design Position
WalkLang stays explicit.
- Keep imports visible.
- Keep side effects visible through
do:or another documented effect form. - Keep calls namespaced.
- Do not add hidden preludes, implicit imports, operator overloading, exceptions,
- Do not claim stable behavior until
docs/SPEC.md,docs/STDLIB.md, tests,
broad macros, or reflection-heavy runtime behavior in this track.
generated docs, native execution, status, release notes, and release artifacts prove it.
Status
| Item | Status | Notes | | --- | --- | --- | | defer: scope cleanup | Implemented | New draft statement form. | | Result-value policy | Implemented | Recoverable result behavior standardized in docs and conformance tests. | | Package collections | Implemented | Explicit import roots documented; reserved roots enforced. | | Build modes | Implemented | Debug/release mode selection is explicit and testable. |
One-Pass Scope
1. defer: Scope Cleanup
Goal: add explicit cleanup that runs at scope exit without hidden destructors.
Required first-pass syntax:
imp: term
imp: io
do: term.color('red')
defer: do term.reset()
do: io.write_line('error')
Required behavior:
defer:is draft until promoted by a later compatibility decision.- A first-pass deferred statement may only be
do: module.effect(args). defer:may appear anywhere a statement is allowed insidemain,func:,- Deferred statements are owned by the lexical block that contains them.
- Deferred statements execute in last-in, first-out order when that block exits.
- Deferred statements execute on normal fallthrough and before a
return:leaves - A
defer:inside a loop body is scoped to that iteration's loop-body block and - Deferred effect-call arguments are evaluated when the
defer:statement is - If a deferred effect runtime-stops, native execution stops immediately; later
if:, while:, for:, and test: blocks.
the owning block or any parent block.
runs before the next iteration starts or before the loop exits.
encountered. The cleanup call uses those captured values at scope exit.
deferred effects are not guaranteed to run.
Required diagnostics:
- Reject
defer:at top level in imported module files unless the module file is - Reject non-effect calls:
type error: defer requires do: effect call. - Reject non-call bodies such as
defer: out: 'x',defer: return: 1, - Reject unknown modules, unimported modules, bad argument counts, and bad
being compiled as an entry or test source.
defer: var: x = 1, and nested defer: defer: ....
argument types through the existing module-call diagnostics.
Proof required:
- Parser/formatter test for
defer: do term.reset(). - Checker fail fixtures for each invalid form above.
- Native runtime test proving LIFO order.
- Native runtime test proving
defer:runs before earlyreturn:. - Native runtime test proving loop-body defers run once per iteration.
- Generated C snapshot update when emitter output changes.
2. Result Values Instead Of Exceptions
Goal: make recoverable failure explicit data, not hidden control flow.
Required first-pass scope:
- Do not add exceptions.
- Do not add a generic
Result[T]type in this pass. - Keep current concrete result structs, such as
IOReadResult, - Promote the recoverable-result shape to a documented language design rule:
ParseIntResult, FileReadResult, and HttpResult.
ok bool
value T
error string
Required behavior:
okis true only whenvalueis meaningful.erroris''on success.erroris a short, stable, machine-testable code on ordinary recoverable- Allocation failure, invalid compiler state, and unrecoverable backend/runtime
- APIs that are intended to be recoverable must not runtime-stop for ordinary
- APIs that intentionally fail-stop must say so in
docs/STDLIB.md.
failure.
failures may still runtime-stop.
documented failures.
Required diagnostics and docs:
docs/LANGUAGE_CONCEPTS.mdmust define recoverable result data separately fromdocs/DESIGN_RULES.mdmust make result values the preferred recoverable-errordocs/STDLIB.mdmust label each draft IO/process/file/json/term/http API asdocs/ERRORS.mdmust keep compiler diagnostics, failed assertions,
runtime failure.
policy.
recoverable or fail-stop.
recoverable result data, and runtime failures distinct.
Proof required:
- Native tests proving representative recoverable APIs return
ok falseand a - Native tests proving representative fail-stop APIs still produce the documented
- Conformance test proving docs and current runtime behavior match for at least
stable error string for ordinary failures.
walk runtime error: ... message.
one io, one file, one parse, and one http result shape.
3. Package Collections
Goal: formalize explicit import roots without adding hidden package behavior.
Required first-pass scope:
- Do not change import syntax.
- Keep user, package, and built-in imports explicit through
imp:. - Treat the first segment of a dotted package import as the collection root.
- Keep existing package imports such as
imp: geometry.corevalid. - Reserve
stdas a future first-party collection root. - Reject publishing a user package named
std. - Do not move current built-in modules from
math,string,array,time,
random, testing, or draft module names into std.* in this pass.
Required package model:
imp: geometry.core
collection root: geometry
module path: geometry/core.walk
generated C prefix: geometry__core__
Required diagnostics:
walk package init stdmust fail with a clear reserved-name diagnostic.walk package publishmust reject package names reserved for current or future- Import-cycle and module-not-found diagnostics must keep naming dotted package
built-in roots.
modules precisely.
Proof required:
- Package lifecycle test showing a collection-root package imports and runs.
- Package publish/init tests rejecting
std. - Existing package tests must keep passing.
- Docs must explain collection roots in project and package documentation.
4. First-Class Build Modes
Goal: make debug and release behavior explicit in CLI, project config, docs, and generated native builds.
Required first-pass CLI:
walk build --mode debug
walk build --mode release
walk build --debug
walk build --release
walk run --mode debug
walk run --mode release
walk run --debug
walk run --release
Required compatibility:
- Keep
--releaseas a supported alias for--mode release. - Add
--debugas a supported alias for--mode debug. - Default mode is
debug. - Project config may use
mode = "debug"ormode = "release"under[build]. - Existing
release = true|falseremains supported for compatibility. - If both
modeandreleaseappear inwalk.toml,modewins and a warning - CLI mode wins over project config.
- Reject conflicting CLI flags such as
--debug --releaseor
is emitted.
--mode debug --release.
Required native behavior:
- Debug mode emits native compiler flags suitable for inspection:
- Release mode keeps current behavior:
- User-supplied
--cflagvalues append after mode flags. - The generated C remains inspectable in both modes.
-g -O0.
-O3 -DNDEBUG.
Required diagnostics:
- Unknown mode error:
unknown build mode "<value>". - Conflict error:
build mode conflict: choose one of debug or release. - Invalid project config error must point at
[build].mode.
Proof required:
- CLI parser tests for
--mode, aliases, defaults, and conflicts. - Project config tests for
mode, compatibility withrelease, warning when - Native build test proving debug flags are present.
- Existing release test proving
-O3 -DNDEBUGremains present. - Docs updates in project, runtime, architecture, and
both are present, and CLI override.
release notes.
Required One-Pass Execution Order
Follow this order in one branch and one release slice:
1. Read this file, docs/LANGUAGE_CONCEPTS.md, docs/DESIGN_RULES.md, docs/SPEC.md, docs/STDLIB.md, project docs, runtime docs, IO_PLAN.md, and the current checker/parser/emitter/package code. 2. Add failing tests for all four items before implementation. 3. Implement parser, AST, formatter, checker, emitter/runtime, CLI, project config, package, and docs changes needed by those tests. 4. Update generated snapshots and generated docs only after source docs and code behavior agree. 5. Run the full verification gate below. 6. Update docs/STATUS.md and docs/RELEASE_NOTES.md with exact commands and outcomes. 7. Commit, tag, publish, and refresh the local installed walk binary following the repo release flow.
Do not split this into separate defer/result/package/build-mode phases. If the slice becomes too large to finish safely, stop before partial implementation, record the blocker in this file, and leave tests/docs showing the exact boundary.
Verification Gate
Minimum commands before this track can be marked complete:
make clean
make walk WALK_VERSION=<version>
make test
make conformance WALK_VERSION=<version>
./build/walk version
./build/walk check --warnings=error tests/pass/walk_tests.walk
WALK_BIN=$PWD/build/walk scripts/stress-compatibility.sh
scripts/build-docs-site.sh
scripts/check-docs-site.sh
make release VERSION=<version> OUT=<temp>/release
wc -l <temp>/release/SHA256SUMS
git diff --check
Also run focused tests added for this track and record their exact names in docs/STATUS.md.
Status Log
- 2026-05-25: Track opened from design discussion. No implementation has started.
- 2026-05-25: Track implemented. Draft
defer:cleanup, recoverable-result
Current decision: keep WalkLang explicit and implement these four items as one future release slice.
policy docs, package collection-root enforcement, and first-class build modes are covered by focused tests and the release verification gate recorded in docs/STATUS.md.