From 2233a6d2058c4169717a842406c478601992208a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 05:46:53 +0000 Subject: [PATCH 1/4] docs: add No-Unlearning Invariant as overarching design law Codify the beginner-to-expert gradient rule that governs WFL's dual goal of being both a first language and production-capable. Every feature's beginner form and expert form must be the same form, or connected by a smooth path with nothing to unlearn. Added to the foundation doc and to CLAUDE.md as a design law that takes precedence when principles conflict. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012MQ2S4bPNRchun4Fj2PFdq --- CLAUDE.md | 7 +++++++ Docs/wfl-foundation.md | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 009385dd..5037a658 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,6 +25,13 @@ These 19 principles are the foundation of WFL's design. Every language, document 18. **Encouragement of Best Practices** — Promote standards that yield high-quality, maintainable code. 19. **Avoidance of Unnecessary Conventions** — Challenge legacy conventions (e.g., mandatory semicolons) that lack clear justification. +### The No-Unlearning Invariant (Overarching Design Law) +WFL is deliberately both a "my first language" and a language strong enough for production. This only works as a *gradient*, not a *compromise* — the beginner path must be a strict subset of the expert path, with no cliffs between them. When principles appear to conflict, this invariant takes precedence: + +> **For every feature, the beginner form and the expert form must be the same form, or connected by a smooth path with nothing to unlearn.** + +Apply it as a test on every language, docs, or tooling change: if a beginner learns a habit that a production user must later undo — or must work around the language to do the most natural thing — that is a crack in the tightrope to fix, not to document. Terser expert forms are welcome only when a beginner can grow into them without unlearning the simple form. Full description in `Docs/wfl-foundation.md`. + ## Project Structure & Modules - `src/`: Core compiler/runtime (`main.rs`, `lib.rs`, `repl.rs`, `builtins.rs`). - `crates/`: Internal crates (e.g., `wfl_core`). diff --git a/Docs/wfl-foundation.md b/Docs/wfl-foundation.md index 048203f9..5a4fd947 100644 --- a/Docs/wfl-foundation.md +++ b/Docs/wfl-foundation.md @@ -98,6 +98,22 @@ The following principles have been refined and expanded to enhance WFL’s acces Description: Challenge traditional programming conventions that rely on special characters or legacy practices without clear justification (e.g., avoiding mandatory semicolons). Goal: Innovate language design to align with natural communication and modern needs. +The No-Unlearning Invariant (Overarching Design Law) +WFL is deliberately both a "my first language" and a language strong enough for production. That dual goal only works as a *gradient*, not a *compromise*: the beginner path must be a strict subset of the expert path, with one continuous rope between them and no cliffs. The invariant that protects this — and that takes precedence when principles appear to conflict — is: + + For every feature, the beginner form and the expert form must be the same form, or connected by a smooth path with nothing to unlearn. + +Why it matters: the failure mode of a "both" language is a design that averages the two audiences and serves neither. The success mode (as Python demonstrated) is one where nothing a learner picks up on day one has to be undone on day one-thousand. Any place a beginner learns a habit that an expert must unlearn is a crack in the tightrope. + +How to apply it: + + Feature test: When adding or changing a feature, ask "does the beginner form differ from the expert form, and if so, is the path between them smooth or a cliff?" A terser expert form is welcome only if a beginner can grow into it without unlearning the simple form. + Verbosity: Natural-language verbosity is fine as long as denser expert forms are the same language, reached by growth rather than replacement. + No day-one cliffs: Requiring a beginner to work around the language to do the most natural thing (e.g., naming a variable) is an invariant violation to fix, not to document. + Consistency of knowledge: Prefer defaults that teach habits an expert keeps. Avoid teaching a beginner an approach that a production user must later abandon. + +This invariant refines principles 11 (Balanced Simplicity and Power) and 16 (Gradual Learning Curve) into a single testable rule that can actually say "no." + Key Enhancements in Version 2 This v2 spec refines WFL’s principles based on research and analysis: From 1e3bc8bb3385fc5385a5be6e4c1043d1484a3336 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 06:03:38 +0000 Subject: [PATCH 2/4] docs: add route construct design spec + interim routing pattern example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design specification for a natural-language 'route'/'when' dispatch construct (flagship: web-server routing). Grounds the design in existing internals — actions are already first-class Value::Function and the interpreter calls function values via FunctionCall — so Level 1 lowers to the existing check-if chain with no runtime changes, and Level 2 dispatch reuses machinery that already exists. Includes No-Unlearning Invariant analysis, pattern table, desugaring, security notes, and a phased TDD plan. Adds TestPrograms/route_interim_pattern.wfl demonstrating the ships-today pattern (allowlist + content_type_for helper) that collapses repetitive asset routes using only existing features. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012MQ2S4bPNRchun4Fj2PFdq --- Docs/development/route-construct-design.md | 282 +++++++++++++++++++++ TestPrograms/route_interim_pattern.wfl | 49 ++++ 2 files changed, 331 insertions(+) create mode 100644 Docs/development/route-construct-design.md create mode 100644 TestPrograms/route_interim_pattern.wfl diff --git a/Docs/development/route-construct-design.md b/Docs/development/route-construct-design.md new file mode 100644 index 00000000..92355979 --- /dev/null +++ b/Docs/development/route-construct-design.md @@ -0,0 +1,282 @@ +# Design: The `route` Construct + +**Status:** Proposed (design/spec — not yet implemented) +**Author:** WFL design discussion +**Applies to:** Language + parser; flagship use is web-server request routing. + +## Motivation + +Dispatching on a value is one of the most common shapes in real WFL programs, +and today the only tool for it is a long `otherwise check if` chain. A typical +web server's request handler looks like this: + +```wfl +check if path is equal to "/checkout/start": + store api_out as call handle_checkout_start with body and secret and req_now and site_db + respond to req with api_out and content_type "text/html" +otherwise check if path is equal to "/checkout/complete": + store api_out as call handle_checkout_complete with body and secret and req_now and site_db + respond to req with api_out and content_type "text/html" +otherwise check if path is equal to "/pricing": + store body_html as call pricing_page + respond to req with body_html and content_type "text/html" +otherwise check if path is equal to "/style.css": + store asset_data as call read_public with "style.css" + respond to req with asset_data and content_type "text/css" +... (a dozen more) ... +otherwise: + store body_html as call not_found_page + respond to req with body_html and status 404 and content_type "text/html" +end check +``` + +Three problems: + +1. **The subject repeats on every arm.** `path is equal to` appears N times; the + thing that varies — the value — is buried in the middle of each line. +2. **Mechanical arms drown out the contract.** Asset routes (`/style.css`, SVGs) + are pure boilerplate: the filename is the path minus its slash and the content + type follows the extension. They add vertical noise around the routes that + actually carry logic. +3. **There is no natural "dispatch on a value" form.** WFL has no `switch`/`match`; + the `otherwise check if` chain is the workaround, and it reads as a workaround. + +## Non-goals + +- We do **not** hide the list of routes behind a registry or reflection. The list + of URLs an app answers is its contract and must stay readable and greppable + (Foundation principles #3 Readability, #11 Balanced Simplicity & Power). `route` + makes the list *cleaner*, never *invisible*. +- We do not change `check if` semantics. `route` is additive. + +## Foundation alignment — the No-Unlearning Invariant + +Per `Docs/wfl-foundation.md`, the governing test is: + +> For every feature, the beginner form and the expert form must be the same form, +> or connected by a smooth path with nothing to unlearn. + +`route` is designed as a **gradient**, not a new dialect: + +- The head `when "/pricing":` maps one-to-one onto the beginner's existing mental + model of `check if path is equal to "/pricing":`. `otherwise` is the *same word* + they already know. +- The block body of a `when` arm is ordinary WFL statements — the exact same + statements the beginner already writes. Nothing inside an arm is new. +- The terser expert forms (declarative arms, below) are **optional** and reachable + by growth. A beginner can always fall back to a full statement body, and an + expert form never invalidates the simple form. + +A beginner who only knows `check if` can read a `route` block on first sight, and +an expert can compress without the beginner having to unlearn anything. The +invariant holds. + +## Syntax + +### Level 1 — imperative arms (beginner; zero prerequisites) + +```wfl +route path: + when "/": + store body_html as call home_page + respond to req with body_html and content_type "text/html" + when "/health": + respond to req with "OK" and content_type "text/plain" + otherwise: + store body_html as call not_found_page + respond to req with body_html and status 404 and content_type "text/html" +end route +``` + +- `route :` — `` is any expression (here the variable `path`). +- `when :` — a block-bodied arm; runs if `` matches the subject. +- `otherwise:` — the default arm (optional; if omitted and nothing matches, the + `route` is a no-op, matching `check if` semantics). +- `end route` — closes the block, consistent with `end check` / `end action`. + +This is **pure syntactic sugar** over the existing `check if` chain (see +Desugaring). It needs no analyzer, type-checker, or interpreter changes. + +### Patterns + +| Pattern form | Matches when … | Desugars to | +|----------------------------------|-------------------------------------------------|-------------| +| `when "/pricing":` | subject `is equal to` the value | `is equal to` | +| `when "/a" or "/b":` | subject equals any listed value | `or`-chain of equalities | +| `when starts with "/api/":` | subject (text) starts with the prefix | `starts with` | +| `when ends with ".css":` | subject (text) ends with the suffix | `ends with` | +| `when contains "admin":` | subject (text) contains the substring | `contains` | +| `when one of asset_files:` | subject is a member of the list | list `contains` | +| `otherwise:` | no earlier arm matched | trailing `otherwise` | + +All pattern forms reuse operators WFL already has, so nothing new must be learned +to read them. + +### Level 2 — declarative arms (expert; optional response shorthands) + +For the extremely common "match a path, produce a response" shape, `route` offers +one-line arms: + +```wfl +route path: + when "/pricing" show page pricing_page + when "/license" show page license_page + when "/style.css" serve asset "style.css" + when starts with "/api/" call api_router with req + otherwise show page not_found_page with status 404 +end route +``` + +- `show page ` → calls `` and responds with `content_type "text/html"`. +- `serve asset ` → reads `` from the public directory and responds with + the content type inferred from its extension (safe: name is not a caller-supplied + path; see Security). +- `call [with ]` → calls `` and responds with its result. +- Any arm may add `with status ` and/or `with content_type `. + +These shorthands desugar to the same statements you would have written by hand. +They read as English to a beginner and are droppable back to full block bodies at +any time, satisfying the invariant. + +**Feasibility note:** `show page pricing_page` names an action as a value. WFL +already represents actions as first-class `Value::Function` and the interpreter +already calls a function value through `Expression::FunctionCall` +(`src/interpreter/mod.rs`). So Level 2 does **not** require a new dispatch engine — +only surface syntax that references an action by name and emits a `FunctionCall`. + +## Desugaring (implementation strategy) + +The lowest-risk, most backward-compatible implementation lowers `route` **in the +parser** into the AST WFL already executes. No new runtime behavior is introduced, +which keeps the change small and keeps every existing `TestPrograms/` program +untouched. + +``` +route : + when P1: B1 + when P2: B2 + otherwise: Bd +end route +``` + +lowers to: + +``` +check if : + B1 +otherwise check if : + B2 +otherwise: + Bd +end check +``` + +where `` expands per the pattern table above. Level 2 arm bodies lower +to the equivalent `store … as call …` + `respond …` statements. + +Two viable lowerings: + +1. **Parser desugaring (recommended for Level 1):** `parse_route` builds the + existing `Statement::IfStatement` chain directly. Analyzer, type checker, and + interpreter need no changes. Fastest path to a correct, safe feature. +2. **Dedicated AST node (`Statement::Route { subject, arms, default }`):** cleaner + for LSP/tooling (hover, folding, "list all routes") and better error messages, + at the cost of touching analyzer + interpreter. Recommended as a follow-up once + Level 1 sugar is proven, so tooling can special-case routes. + +Start with (1); migrate to (2) if tooling wants first-class route awareness. + +## Security + +`serve asset ` must only ever serve from the configured public directory and +must reject `..`/absolute paths — the `name` is author-provided in the source, but +the desugaring helper should still sandbox to prevent a future refactor from +piping a request path straight through. This preserves Foundation principle #8 +(secure by default) and mirrors the existing `read_public`/allowlist pattern in +`comprehensive_web_server_demo.wfl`. + +## Other uses beyond web routing + +Because the subject is any expression and patterns cover equality, membership, and +text tests, `route` is really WFL's natural-language `match`/`switch`: + +```wfl +route status_code: + when 200: + display "OK" + when 404 or 410: + display "Gone" + otherwise: + display "Unexpected status" +end route +``` + +Web routing is the flagship use and the reason for the keyword's name, but the +construct is general. + +## Reserved-keyword impact + +`route` becomes a structural keyword; `when` becomes contextual (it already reads +naturally and does not collide with common identifiers). Per the two-tiered keyword +policy, both `Docs/reference/keyword-reference.md` and +`Docs/reference/reserved-keywords.md` must be updated when this lands, and the +total keyword count adjusted. Follow the No-Unlearning Invariant here too: prefer +`when` remaining usable as an identifier outside a `route` head so beginners are +never told "you can't name it that." + +## Phased implementation plan (TDD) + +Per `CLAUDE.md`, write failing tests first at each phase. + +1. **Lexer:** add `route`/`when` tokens (keep `when` contextual). Tests: + `src/lexer` token tests. +2. **Parser (Level 1):** `parse_route` desugars to the `IfStatement` chain. + Tests: parser unit tests asserting the lowered AST equals the hand-written + `check if` chain; `--parse` snapshot. +3. **End-to-end (Level 1):** `TestPrograms/route_comprehensive.wfl` covering every + pattern form, run under the release build; add to the docs-examples manifest. +4. **Patterns:** add `starts with` / `ends with` / `contains` / `one of` heads. +5. **Level 2 shorthands:** `show page` / `serve asset` / `call` arm forms + + `with status` / `with content_type` modifiers. +6. **User docs:** once validated, promote examples into + `Docs/04-advanced-features/routing.md` and cross-link from + `Docs/03-language-basics/control-flow.md`; update keyword references. +7. **LSP / dedicated AST (optional follow-up):** `Statement::Route` node for route + awareness in tooling. + +## Interim: what to do today (no language change) + +Until `route` lands, the same block can be tightened with existing features by +collapsing the mechanical arms and factoring the shared response tail. The asset +cluster reduces from one arm per file to a single allowlisted arm plus a +content-type helper: + +```wfl +store asset_files as ["style.css", "logbie-wordmark.svg", + "logbie-wordmark-on-forest.svg", "bie.svg", "bie-waiting.svg"] + +define action called content_type_for with name: + check if name ends with ".css": + return "text/css" + otherwise check if name ends with ".svg": + return "image/svg+xml" + otherwise check if name ends with ".json": + return "application/json" + otherwise: + return "text/html" + end check +end action +``` + +```wfl +otherwise check if contains of asset_files and substring of path from 1: + store filename as substring of path from 1 + store asset_data as call read_public with filename + store ctype as call content_type_for with filename + respond to req with asset_data and content_type ctype +``` + +This is the drop-in for the six asset arms; the API and page arms stay explicit +because they *are* the route contract. See +`TestPrograms/route_interim_pattern.wfl` for a validated demonstration of the +reusable pieces (`content_type_for` + allowlist dispatch). diff --git a/TestPrograms/route_interim_pattern.wfl b/TestPrograms/route_interim_pattern.wfl new file mode 100644 index 00000000..022a6f85 --- /dev/null +++ b/TestPrograms/route_interim_pattern.wfl @@ -0,0 +1,49 @@ +// route_interim_pattern.wfl +// Demonstrates the "ships-today" pattern for collapsing repetitive asset routes +// using only existing WFL features: an allowlist plus a content-type helper. +// This is the reusable core that the proposed `route` construct will formalize. +// See Docs/development/route-construct-design.md. + +store asset_files as ["style.css", "logo.svg", "bie.svg", "data.json"] + +// Content type derived from the file extension, written once instead of per-route. +define action called content_type_for with name: + check if name ends with ".css": + return "text/css" + otherwise check if name ends with ".svg": + return "image/svg+xml" + otherwise check if name ends with ".json": + return "application/json" + otherwise: + return "text/html" + end check +end action + +// One arm replaces the six near-identical asset arms. The allowlist keeps it +// secure: only files explicitly listed are ever served. +define action called serve as text with path: + store filename as substring of path from 1 + check if contains of asset_files and filename: + store ctype as call content_type_for with filename + return "200 " with path with " -> " with filename with " (" with ctype with ")" + otherwise: + return "404 " with path + end check +end action + +display "=== Asset routing via allowlist + content_type_for ===" + +store r1 as call serve with "/style.css" +display r1 + +store r2 as call serve with "/logo.svg" +display r2 + +store r3 as call serve with "/bie.svg" +display r3 + +store r4 as call serve with "/data.json" +display r4 + +store r5 as call serve with "/secret.env" +display r5 From 59136ea4d863ce007c3ffa7a1ff3b47a94286f4a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 06:08:07 +0000 Subject: [PATCH 3/4] fix: validate route interim example against release build; correct doc syntax Ran the interim example under the release binary and fixed real syntax issues the initial draft assumed but that do not hold under the full pipeline: - 'ends with' / 'starts with' get swallowed as multi-word identifiers at statement level and fault in analysis; replaced with 'contains'. - 'substring of X from N' fails type checking (substring needs 3 args); key the allowlist on the full path instead, and document the 3-arg 'substring of X and 1 and length of X' form where stripping is needed. - removed an invalid 'as text' return annotation from the action header. Example now runs clean (exit 0) with verified expected output. Design doc records the operator-status finding as motivation and a prerequisite for the route construct's prefix/suffix pattern heads. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012MQ2S4bPNRchun4Fj2PFdq --- Docs/development/route-construct-design.md | 82 ++++++++++++++++------ TestPrograms/route_interim_pattern.wfl | 27 ++++--- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/Docs/development/route-construct-design.md b/Docs/development/route-construct-design.md index 92355979..02c542d7 100644 --- a/Docs/development/route-construct-design.md +++ b/Docs/development/route-construct-design.md @@ -101,16 +101,33 @@ Desugaring). It needs no analyzer, type-checker, or interpreter changes. | Pattern form | Matches when … | Desugars to | |----------------------------------|-------------------------------------------------|-------------| -| `when "/pricing":` | subject `is equal to` the value | `is equal to` | -| `when "/a" or "/b":` | subject equals any listed value | `or`-chain of equalities | -| `when starts with "/api/":` | subject (text) starts with the prefix | `starts with` | -| `when ends with ".css":` | subject (text) ends with the suffix | `ends with` | -| `when contains "admin":` | subject (text) contains the substring | `contains` | -| `when one of asset_files:` | subject is a member of the list | list `contains` | -| `otherwise:` | no earlier arm matched | trailing `otherwise` | - -All pattern forms reuse operators WFL already has, so nothing new must be learned -to read them. +| Pattern form | Matches when … | Desugars to | Operator status today | +|----------------------------------|-------------------------------------------------|-------------|-----------------------| +| `when "/pricing":` | subject `is equal to` the value | `is equal to` | works | +| `when "/a" or "/b":` | subject equals any listed value | `or`-chain of equalities | works | +| `when contains "admin":` | subject (text) contains the substring | `contains of subject and "admin"` | works | +| `when one of asset_files:` | subject is a member of the list | `contains of asset_files and subject` | works | +| `when starts with "/api/":` | subject (text) starts with the prefix | prefix test | **needs operator work — see below** | +| `when ends with ".css":` | subject (text) ends with the suffix | suffix test | **needs operator work — see below** | +| `otherwise:` | no earlier arm matched | trailing `otherwise` | works | + +**Operator-status finding (validated against the release build):** `contains` +(both text-substring and list-membership) works reliably as `contains of X and Y`. +But `X starts with "…"` and `X ends with "…"` are **not** reliable at statement +level today: the parser's multi-word identifier rule swallows the operator, lexing +`path ends with ".css"` as the identifier `path ends` followed by `with ".css"`, +which then fails analysis ("Variable 'path ends' is not defined"). Several existing +demo programs (e.g. `comprehensive_web_server_demo.wfl`) *contain* these forms but +only survive `--analyze`'s leniency; they fault under the full pipeline. Likewise +`substring of X from N` fails type checking because the `substring` builtin requires +three arguments (`substring of X and start and end`). + +This is itself an argument for `route`: prefix/suffix matching is exactly what +routing needs, and a first-class `when starts with …` head is a clean place to give +these operators real token support (a dedicated `KeywordStartsWith`/`KeywordEndsWith` +or a proper infix parse) instead of relying on the fragile bareword path. Until that +lands, the `contains`/equality/membership patterns above are the reliable subset, +and the interim guidance below uses only those. ### Level 2 — declarative arms (expert; optional response shorthands) @@ -235,7 +252,10 @@ Per `CLAUDE.md`, write failing tests first at each phase. `check if` chain; `--parse` snapshot. 3. **End-to-end (Level 1):** `TestPrograms/route_comprehensive.wfl` covering every pattern form, run under the release build; add to the docs-examples manifest. -4. **Patterns:** add `starts with` / `ends with` / `contains` / `one of` heads. +4. **Patterns:** add `contains` and `one of` heads first (operators already work), + then land real `starts with` / `ends with` operator support (dedicated tokens or + proper infix parse) and expose them as `when` heads — see the operator-status + finding above. 5. **Level 2 shorthands:** `show page` / `serve asset` / `call` arm forms + `with status` / `with content_type` modifiers. 6. **User docs:** once validated, promote examples into @@ -252,15 +272,15 @@ cluster reduces from one arm per file to a single allowlisted arm plus a content-type helper: ```wfl -store asset_files as ["style.css", "logbie-wordmark.svg", - "logbie-wordmark-on-forest.svg", "bie.svg", "bie-waiting.svg"] +store asset_paths as ["/style.css", "/logbie-wordmark.svg", + "/logbie-wordmark-on-forest.svg", "/bie.svg", "/bie-waiting.svg"] define action called content_type_for with name: - check if name ends with ".css": + check if contains of name and ".css": return "text/css" - otherwise check if name ends with ".svg": + otherwise check if contains of name and ".svg": return "image/svg+xml" - otherwise check if name ends with ".json": + otherwise check if contains of name and ".json": return "application/json" otherwise: return "text/html" @@ -269,14 +289,30 @@ end action ``` ```wfl -otherwise check if contains of asset_files and substring of path from 1: - store filename as substring of path from 1 - store asset_data as call read_public with filename - store ctype as call content_type_for with filename +otherwise check if contains of asset_paths and path: + store asset_data as call read_public with path + store ctype as call content_type_for with path respond to req with asset_data and content_type ctype ``` This is the drop-in for the six asset arms; the API and page arms stay explicit -because they *are* the route contract. See -`TestPrograms/route_interim_pattern.wfl` for a validated demonstration of the -reusable pieces (`content_type_for` + allowlist dispatch). +because they *are* the route contract. Keying the allowlist on the full path (with +its leading slash) avoids `substring`, and detecting the type with `contains` +avoids the unreliable `ends with` — both choices use only operators verified to +work under the full pipeline. See `TestPrograms/route_interim_pattern.wfl` for a +runnable, release-build-validated demonstration (`content_type_for` + allowlist +dispatch), whose expected output is: + +``` +=== Asset routing via allowlist + content_type_for === +200 /style.css (text/css) +200 /logo.svg (image/svg+xml) +200 /bie.svg (image/svg+xml) +200 /data.json (application/json) +404 /secret.env +``` + +> Note: if your `read_public` helper expects a bare filename rather than a +> URL path, strip the leading slash inside the arm with the 3-argument +> `substring of path and 1 and length of path` (not `substring … from N`, +> which does not type-check). diff --git a/TestPrograms/route_interim_pattern.wfl b/TestPrograms/route_interim_pattern.wfl index 022a6f85..89f367a6 100644 --- a/TestPrograms/route_interim_pattern.wfl +++ b/TestPrograms/route_interim_pattern.wfl @@ -1,18 +1,18 @@ // route_interim_pattern.wfl // Demonstrates the "ships-today" pattern for collapsing repetitive asset routes -// using only existing WFL features: an allowlist plus a content-type helper. -// This is the reusable core that the proposed `route` construct will formalize. -// See Docs/development/route-construct-design.md. +// using only confirmed-working WFL features: an allowlist plus a content-type +// helper built on `contains`. This is the reusable core that the proposed +// `route` construct will formalize. See Docs/development/route-construct-design.md. -store asset_files as ["style.css", "logo.svg", "bie.svg", "data.json"] +store asset_paths as ["/style.css", "/logo.svg", "/bie.svg", "/data.json"] -// Content type derived from the file extension, written once instead of per-route. +// Content type derived from the path, written once instead of once per route. define action called content_type_for with name: - check if name ends with ".css": + check if contains of name and ".css": return "text/css" - otherwise check if name ends with ".svg": + otherwise check if contains of name and ".svg": return "image/svg+xml" - otherwise check if name ends with ".json": + otherwise check if contains of name and ".json": return "application/json" otherwise: return "text/html" @@ -20,12 +20,11 @@ define action called content_type_for with name: end action // One arm replaces the six near-identical asset arms. The allowlist keeps it -// secure: only files explicitly listed are ever served. -define action called serve as text with path: - store filename as substring of path from 1 - check if contains of asset_files and filename: - store ctype as call content_type_for with filename - return "200 " with path with " -> " with filename with " (" with ctype with ")" +// secure: only paths explicitly listed are ever served. +define action called serve with path: + check if contains of asset_paths and path: + store ctype as call content_type_for with path + return "200 " with path with " (" with ctype with ")" otherwise: return "404 " with path end check From b4b64a59c6821363130299191407648c0ecf979b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 06:24:31 +0000 Subject: [PATCH 4/4] docs: address review feedback (table header, subset wording, issue ref) - Remove duplicated markdown table header in the route design doc's pattern table (rendered incorrectly). - Soften 'strict subset' to 'subset' in the No-Unlearning Invariant text in both CLAUDE.md and the foundation doc: the invariant explicitly allows the beginner and expert forms to be identical, which a strict (proper) subset would exclude. - Link the operator-status finding to tracking issue #566. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_012MQ2S4bPNRchun4Fj2PFdq --- CLAUDE.md | 2 +- Docs/development/route-construct-design.md | 8 +++----- Docs/wfl-foundation.md | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5037a658..86045de4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,7 +26,7 @@ These 19 principles are the foundation of WFL's design. Every language, document 19. **Avoidance of Unnecessary Conventions** — Challenge legacy conventions (e.g., mandatory semicolons) that lack clear justification. ### The No-Unlearning Invariant (Overarching Design Law) -WFL is deliberately both a "my first language" and a language strong enough for production. This only works as a *gradient*, not a *compromise* — the beginner path must be a strict subset of the expert path, with no cliffs between them. When principles appear to conflict, this invariant takes precedence: +WFL is deliberately both a "my first language" and a language strong enough for production. This only works as a *gradient*, not a *compromise* — the beginner path must be a subset of the expert path, with no cliffs between them. When principles appear to conflict, this invariant takes precedence: > **For every feature, the beginner form and the expert form must be the same form, or connected by a smooth path with nothing to unlearn.** diff --git a/Docs/development/route-construct-design.md b/Docs/development/route-construct-design.md index 02c542d7..be43e31d 100644 --- a/Docs/development/route-construct-design.md +++ b/Docs/development/route-construct-design.md @@ -99,8 +99,6 @@ Desugaring). It needs no analyzer, type-checker, or interpreter changes. ### Patterns -| Pattern form | Matches when … | Desugars to | -|----------------------------------|-------------------------------------------------|-------------| | Pattern form | Matches when … | Desugars to | Operator status today | |----------------------------------|-------------------------------------------------|-------------|-----------------------| | `when "/pricing":` | subject `is equal to` the value | `is equal to` | works | @@ -122,9 +120,9 @@ only survive `--analyze`'s leniency; they fault under the full pipeline. Likewis `substring of X from N` fails type checking because the `substring` builtin requires three arguments (`substring of X and start and end`). -This is itself an argument for `route`: prefix/suffix matching is exactly what -routing needs, and a first-class `when starts with …` head is a clean place to give -these operators real token support (a dedicated `KeywordStartsWith`/`KeywordEndsWith` +This is tracked as issue #566. It is itself an argument for `route`: prefix/suffix +matching is exactly what routing needs, and a first-class `when starts with …` head +is a clean place to give these operators real token support (a dedicated `KeywordStartsWith`/`KeywordEndsWith` or a proper infix parse) instead of relying on the fragile bareword path. Until that lands, the `contains`/equality/membership patterns above are the reliable subset, and the interim guidance below uses only those. diff --git a/Docs/wfl-foundation.md b/Docs/wfl-foundation.md index 5a4fd947..4de9e86b 100644 --- a/Docs/wfl-foundation.md +++ b/Docs/wfl-foundation.md @@ -99,7 +99,7 @@ The following principles have been refined and expanded to enhance WFL’s acces Goal: Innovate language design to align with natural communication and modern needs. The No-Unlearning Invariant (Overarching Design Law) -WFL is deliberately both a "my first language" and a language strong enough for production. That dual goal only works as a *gradient*, not a *compromise*: the beginner path must be a strict subset of the expert path, with one continuous rope between them and no cliffs. The invariant that protects this — and that takes precedence when principles appear to conflict — is: +WFL is deliberately both a "my first language" and a language strong enough for production. That dual goal only works as a *gradient*, not a *compromise*: the beginner path must be a subset of the expert path (possibly the whole of it), with one continuous rope between them and no cliffs. The invariant that protects this — and that takes precedence when principles appear to conflict — is: For every feature, the beginner form and the expert form must be the same form, or connected by a smooth path with nothing to unlearn.