Files
rose-ash/sx/sx/essays.sx
giles ca8de3be1a Make continuations an optional extension, add special-forms.sx, ellipsis parsing
- Both bootstrappers (JS + Python) now gate shift/reset behind --extensions
  continuations flag. Without it, using reset/shift errors at runtime.
- JS bootstrapper: extracted Continuation/ShiftSignal types, sfReset/sfShift,
  continuation? primitive, and typeOf handling into CONTINUATIONS_JS constant.
  Extension wraps evalList, aserSpecial, and typeOf post-transpilation.
- Python bootstrapper: added special-forms.sx validation cross-check against
  eval.sx dispatch, warns on mismatches.
- Added shared/sx/ref/special-forms.sx: 36 declarative form specs with syntax,
  docs, tail-position, and examples. Used by bootstrappers for validation.
- Added ellipsis (...) support to both parser.py and parser.sx spec.
- Updated continuations essay to reflect optional extension architecture.
- Updated specs page and nav with special-forms.sx entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:44:50 +00:00

262 lines
110 KiB
Plaintext

;; Essay content — static content extracted from essays.py
(defcomp ~essays-index-content ()
(~doc-page :title "Essays"
(div :class "space-y-4"
(p :class "text-lg text-stone-600 mb-4"
"Opinions, rationales, and explorations around SX and the ideas behind it.")
(div :class "space-y-3"
(map (fn (item)
(a :href (get item "href")
:sx-get (get item "href") :sx-target "#main-panel" :sx-select "#main-panel"
:sx-swap "outerHTML" :sx-push-url "true"
:class "block rounded border border-stone-200 p-4 hover:border-violet-300 hover:bg-violet-50 transition-colors"
(div :class "font-semibold text-stone-800" (get item "label"))
(when (get item "summary")
(p :class "text-sm text-stone-500 mt-1" (get item "summary")))))
essays-nav-items)))))
(defcomp ~essay-sx-sucks ()
(~doc-page :title "sx sucks" (~doc-section :title "The parentheses" :id "parens" (p :class "text-stone-600" "S-expressions are parentheses. Lots of parentheses. You thought LISP was dead? No, someone just decided to use it for HTML templates. Your IDE will need a parenthesis counter. Your code reviews will be 40% closing parens. Every merge conflict will be about whether a paren belongs on this line or the next.")) (~doc-section :title "Nobody asked for this" :id "nobody-asked" (p :class "text-stone-600" "The JavaScript ecosystem has React, Vue, Svelte, Solid, Qwik, and approximately 47,000 other frameworks. htmx proved you can skip them all. sx looked at this landscape and said: you know what this needs? A Lisp dialect. For HTML. Over HTTP.") (p :class "text-stone-600" "Nobody was asking for this. The zero GitHub stars confirm it. It is not even on GitHub.")) (~doc-section :title "The author has never written a line of LISP" :id "no-lisp" (p :class "text-stone-600" "The author of sx has never written a single line of actual LISP. Not Common Lisp. Not Scheme. Not Clojure. Not even Emacs Lisp. The entire s-expression evaluator was written by someone whose mental model of LISP comes from reading the first three chapters of SICP and then closing the tab.") (p :class "text-stone-600" "This is like building a sushi restaurant when your only experience with Japanese cuisine is eating supermarket California rolls.")) (~doc-section :title "AI wrote most of it" :id "ai" (p :class "text-stone-600" "A significant portion of sx — the evaluator, the parser, the primitives, the CSS scanner, this very documentation site — was written with AI assistance. The author typed prompts. Claude typed code. This is not artisanal hand-crafted software. This is the software equivalent of a microwave dinner presented on a nice plate.") (p :class "text-stone-600" "He adds features by typing stuff like \"is there rom for macros within sx.js? what benefits m,ight that bring?\", skim-reading the response, and then entering \"crack on then!\" This is not software engineering. This is improv comedy with a compiler.") (p :class "text-stone-600" "Is that bad? Maybe. Is it honest? Yes. Is this paragraph also AI-generated? You will never know.")) (~doc-section :title "No ecosystem" :id "ecosystem" (p :class "text-stone-600" "npm has 2 million packages. PyPI has 500,000. sx has zero packages, zero plugins, zero middleware, zero community, zero Stack Overflow answers, and zero conference talks. If you get stuck, your options are: read the source, or ask the one person who wrote it.") (p :class "text-stone-600" "That person is busy. Good luck.")) (~doc-section :title "Zero jobs" :id "jobs" (p :class "text-stone-600" "Adding sx to your CV will not get you hired. It will get you questioned.") (p :class "text-stone-600" "The interview will end shortly after.")) (~doc-section :title "The creator thinks s-expressions are a personality trait" :id "personality" (p :class "text-stone-600" "Look at this documentation site. It has a violet colour scheme. It has credits to htmx. It has a future possibilities page about hypothetical sx:// protocol schemes. The creator built an entire microservice — with Docker, Redis, and a custom entrypoint script — just to serve documentation about a rendering engine that runs one website.") (p :class "text-stone-600" "This is not engineering. This is a personality disorder expressed in YAML."))))
(defcomp ~essay-why-sexps ()
(~doc-page :title "Why S-Expressions Over HTML Attributes" (~doc-section :title "The problem with HTML attributes" :id "problem" (p :class "text-stone-600" "HTML attributes are strings. You can put anything in a string. htmx puts DSLs in strings — trigger modifiers, swap strategies, CSS selectors. This works but it means you're parsing a language within a language within a language.") (p :class "text-stone-600" "S-expressions are already structured. Keywords are keywords. Lists are lists. Nested expressions nest naturally. There's no need to invent a trigger modifier syntax because the expression language already handles composition.")) (~doc-section :title "Components without a build step" :id "components" (p :class "text-stone-600" "React showed that components are the right abstraction for UI. The price: a build step, a bundler, JSX transpilation. With s-expressions, defcomp is just another form in the language. No transpiler needed. The same source runs on server and client.")) (~doc-section :title "When attributes are better" :id "better" (p :class "text-stone-600" "HTML attributes work in any HTML document. S-expressions need a runtime. If you want progressive enhancement that works with JS disabled, htmx is better. If you want to write HTML by hand in static files, htmx is better. sx only makes sense when you're already rendering server-side and want components."))))
(defcomp ~essay-htmx-react-hybrid ()
(~doc-page :title "The htmx/React Hybrid" (~doc-section :title "Two good ideas" :id "ideas" (p :class "text-stone-600" "htmx: the server should render HTML. The client should swap it in. No client-side routing. No virtual DOM. No state management.") (p :class "text-stone-600" "React: UI should be composed from reusable components with parameters. Components encapsulate structure, style, and behavior.") (p :class "text-stone-600" "sx tries to combine both: server-rendered s-expressions with hypermedia attributes AND a component model with caching and composition.")) (~doc-section :title "What sx keeps from htmx" :id "from-htmx" (ul :class "space-y-2 text-stone-600" (li "Server generates the UI — no client-side data fetching or state") (li "Hypermedia attributes (sx-get, sx-target, sx-swap) on any element") (li "Partial page updates via swap/OOB — no full page reloads") (li "Works with standard HTTP — no WebSocket or custom protocol required"))) (~doc-section :title "What sx adds from React" :id "from-react" (ul :class "space-y-2 text-stone-600" (li "defcomp — named, parameterized, composable components") (li "Client-side rendering — server sends source, client renders DOM") (li "Component caching — definitions cached in localStorage across navigations") (li "On-demand CSS — only ship the rules that are used"))) (~doc-section :title "What sx gives up" :id "gives-up" (ul :class "space-y-2 text-stone-600" (li "No HTML output — sx sends s-expressions, not HTML. JS required.") (li "Custom parser — the client needs sx.js to understand responses") (li "Niche — no ecosystem, no community, no third-party support") (li "Learning curve — s-expression syntax is unfamiliar to most web developers")))))
(defcomp ~essay-on-demand-css ()
(~doc-page :title "On-Demand CSS: Killing the Tailwind Bundle" (~doc-section :title "The problem" :id "problem" (p :class "text-stone-600" "Tailwind CSS generates a utility class for every possible combination. The full CSS file is ~4MB. The purged output for a typical site is 20-50KB. Purging requires a build step that scans your source files for class names. This means: a build tool, a config file, a CI step, and a prayer that the scanner finds all your dynamic classes.")) (~doc-section :title "The sx approach" :id "approach" (p :class "text-stone-600" "sx takes a different path. At server startup, the full Tailwind CSS file is parsed into a dictionary keyed by class name. When rendering a response, sx scans the s-expression source for :class attribute values and looks up only those classes. The result: exact CSS, zero build step.") (p :class "text-stone-600" "Component definitions are pre-scanned at registration time. Page-specific sx is scanned at request time. The union of classes is resolved to CSS rules.")) (~doc-section :title "Incremental delivery" :id "incremental" (p :class "text-stone-600" "After the first page load, the client tracks which CSS classes it already has. On subsequent navigations, it sends a hash of its known classes in the SX-Css header. The server computes the diff and sends only new rules. A typical navigation adds 0-10 new rules — a few hundred bytes at most.")) (~doc-section :title "The tradeoff" :id "tradeoff" (p :class "text-stone-600" "The server holds ~4MB of parsed CSS in memory. Regex scanning is not perfect — dynamically constructed class names will not be found. In practice this rarely matters because sx components use mostly static class strings."))))
(defcomp ~essay-client-reactivity ()
(~doc-page :title "Client Reactivity: The React Question" (~doc-section :title "Server-driven by default" :id "server-driven" (p :class "text-stone-600" "sx is aligned with htmx and LiveView: the server is the source of truth. Every UI state is a URL. Auth is enforced at render time. There are no state synchronization bugs because there is no client state to synchronize. The server renders the UI, the client swaps it in. This is the default, and it works.") (p :class "text-stone-600" "Most web applications do not need client-side reactivity. Forms submit to the server. Navigation loads new pages. Search sends a query and receives results. The server-driven model handles all of this with zero client-side state management.")) (~doc-section :title "The dangerous path" :id "dangerous-path" (p :class "text-stone-600" "The progression is always the same. You add useState for a toggle. Then useEffect for cleanup. Then Context to avoid prop drilling. Then Suspense for async boundaries. Then a state management library because Context rerenders too much. Then you have rebuilt React.") (p :class "text-stone-600" "Every step feels justified in isolation. But each step makes the next one necessary. useState creates the need for useEffect. useEffect creates the need for cleanup. Cleanup creates the need for dependency arrays. Dependency arrays create stale closures. Stale closures create bugs that are nearly impossible to diagnose.") (p :class "text-stone-600" "The useEffect footgun is well-documented. Memory leaks from forgotten cleanup. Race conditions from unmounted component updates. Infinite render loops from dependency array mistakes. These are not edge cases — they are the normal experience of React development.")) (~doc-section :title "What sx already has" :id "what-sx-has" (p :class "text-stone-600" "Before reaching for reactivity, consider what sx provides today:") (div :class "overflow-x-auto mt-4" (table :class "w-full text-sm text-left" (thead (tr :class "border-b border-stone-200" (th :class "py-2 pr-4 font-semibold text-stone-700" "Capability") (th :class "py-2 pr-4 font-semibold text-stone-700" "sx") (th :class "py-2 font-semibold text-stone-700" "React"))) (tbody :class "text-stone-600" (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "Components + props") (td :class "py-2 pr-4" "defcomp + &key") (td :class "py-2" "JSX + props")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "Fragments / conditionals / lists") (td :class "py-2 pr-4" "<>, if/when/cond, map") (td :class "py-2" "<>, ternary, .map()")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "Macros") (td :class "py-2 pr-4" "defmacro") (td :class "py-2" "Nothing equivalent")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "OOB updates / portals") (td :class "py-2 pr-4" "sx-swap-oob") (td :class "py-2" "createPortal")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "DOM reconciliation") (td :class "py-2 pr-4" "morphDOM (id-keyed)") (td :class "py-2" "Virtual DOM diff")) (tr (td :class "py-2 pr-4" "Reactive client state") (td :class "py-2 pr-4 italic" "None (by design)") (td :class "py-2" "useState / useReducer")))))) (~doc-section :title "Tier 1: Targeted escape hatches" :id "tier-1" (p :class "text-stone-600" "Some interactions are too small to justify a server round-trip: toggling a nav menu, switching a gallery image, incrementing a quantity stepper, filtering a client-side list. These need imperative DOM operations, not reactive state.") (p :class "text-stone-600" "Specific primitives for this tier:") (ul :class "space-y-2 text-stone-600 mt-2" (li (code :class "text-violet-700" "(toggle! el \"class\")") " — add/remove a CSS class") (li (code :class "text-violet-700" "(set-attr! el \"attr\" value)") " — set an attribute") (li (code :class "text-violet-700" "(on-event el \"click\" handler)") " — attach an event listener") (li (code :class "text-violet-700" "(timer ms handler)") " — schedule a delayed action")) (p :class "text-stone-600" "These are imperative DOM operations. No reactivity graph. No subscriptions. No dependency tracking. Just do the thing directly.")) (~doc-section :title "Tier 2: Client data primitives" :id "tier-2" (p :class "text-stone-600" "sxEvalAsync() returning Promises. I/O primitives — query, service, frag — dispatch to /api/data/ endpoints. A two-pass async DOM renderer. Pages fetch their own data client-side.") (p :class "text-stone-600" "This tier enables pages that render immediately with a loading skeleton, then populate with data. The server sends the component structure; the client fetches data. Still no reactive state — just async data loading.")) (~doc-section :title "Tier 3: Data-only navigation" :id "tier-3" (p :class "text-stone-600" "The client has page components cached in localStorage. Navigation becomes a data fetch only — no sx source is transferred. defpage registers a component in the page registry. URL pattern matching routes to the right page component.") (p :class "text-stone-600" "Three data delivery modes: server-bundled (sx source + data in one response), client-fetched (component cached, data fetched on mount), and hybrid (server provides initial data, client refreshes).") (p :class "text-stone-600" "This is where sx starts to feel like a SPA — instant navigations, no page reloads, cached components. But still no reactive state management.")) (~doc-section :title "Tier 4: Fine-grained reactivity" :id "tier-4" (p :class "text-stone-600" "Signals and atoms. Dependency tracking. Automatic re-renders when data changes. This is the most dangerous tier because it reintroduces everything sx was designed to avoid.") (p :class "text-stone-600" "When it might be justified: real-time collaborative editing, complex form builders with dozens of interdependent fields, drag-and-drop interfaces with live previews. These are genuinely hard to model as server round-trips.") (p :class "text-stone-600" "The escape hatch: use a Web Component wrapping a reactive library (Preact, Solid, vanilla signals), mounted into the DOM via sx. The reactive island is contained. It does not infect the rest of the application. sx renders the page; the Web Component handles the complex interaction.")) (~doc-section :title "The recommendation" :id "recommendation" (p :class "text-stone-600" "Tier 1 now. Tier 2 next. Tier 3 when defpage coverage is high. Tier 4 probably never.") (p :class "text-stone-600" "Each tier is independently valuable. You do not need Tier 2 to benefit from Tier 1. You do not need Tier 3 to benefit from Tier 2. And you almost certainly do not need Tier 4 at all.") (p :class "text-stone-600" "The entire point of sx is that the server is good at rendering UI. Client reactivity is a last resort, not a starting point."))))
(defcomp ~essay-sx-native ()
(~doc-page :title "SX Native: Beyond the Browser" (~doc-section :title "The thesis" :id "thesis" (p :class "text-stone-600" "sx.js is a ~2,300-line tree-walking interpreter with ~50 primitives. The DOM is just one rendering target. Swap the DOM adapter for a platform-native adapter and you get React Native — but with s-expressions and a 50-primitive surface area.") (p :class "text-stone-600" "The interpreter does not know about HTML. It evaluates expressions, calls primitives, expands macros, and hands render instructions to an adapter. The adapter creates elements. Today that adapter creates DOM nodes. It does not have to.")) (~doc-section :title "Why this isn\'t a WebView" :id "not-webview" (p :class "text-stone-600" "SX Native means the sx evaluator rendering to native UI widgets directly. No DOM. No CSS. No HTML. (button :on-press handler \"Submit\") creates a native UIButton on iOS, a Material Button on Android, a GtkButton on Linux.") (p :class "text-stone-600" "WebView wrappers (Cordova, Capacitor, Electron) ship a browser inside your app. They inherit all browser limitations: memory overhead, no native feel, no platform integration. SX Native has none of these because there is no browser.")) (~doc-section :title "Architecture" :id "architecture" (p :class "text-stone-600" "The architecture splits into shared and platform-specific layers:") (ul :class "space-y-2 text-stone-600 mt-2" (li (strong "Shared (portable):") " Parser, evaluator, all 50+ primitives, component system, macro expansion, closures, component cache") (li (strong "Platform adapters:") " Web DOM, iOS UIKit/SwiftUI, Android Compose, Desktop GTK/Qt, Terminal TUI, WASM")) (p :class "text-stone-600" "Only ~15 rendering primitives need platform-specific implementations. The rest — arithmetic, string operations, list manipulation, higher-order functions, control flow — are pure computation with no platform dependency.")) (~doc-section :title "The primitive contract" :id "primitive-contract" (p :class "text-stone-600" "A platform adapter implements a small interface:") (ul :class "space-y-2 text-stone-600 mt-2" (li (code :class "text-violet-700" "createElement(tag)") " — create a platform widget") (li (code :class "text-violet-700" "createText(str)") " — create a text node") (li (code :class "text-violet-700" "setAttribute(el, key, val)") " — set a property") (li (code :class "text-violet-700" "appendChild(parent, child)") " — attach to tree") (li (code :class "text-violet-700" "addEventListener(el, event, fn)") " — bind interaction") (li (code :class "text-violet-700" "removeChild(parent, child)") " — detach from tree")) (p :class "text-stone-600" "Layout uses a flexbox-like model mapped to native constraint systems. Styling maps a CSS property subset to native appearance APIs. The mapping is lossy but covers the common cases.")) (~doc-section :title "What transfers, what doesn\'t" :id "transfers" (p :class "text-stone-600" "What transfers wholesale: parser, evaluator, all non-DOM primitives, component system (defcomp, defmacro), closures, the component cache, keyword argument handling, list/dict operations.") (p :class "text-stone-600" "What needs replacement: HTML tags become abstract widgets, CSS becomes platform layout, SxEngine fetch/swap/history becomes native navigation, innerHTML/outerHTML have no equivalent.")) (~doc-section :title "Component mapping" :id "component-mapping" (p :class "text-stone-600" "HTML elements map to platform-native widgets:") (div :class "overflow-x-auto mt-4" (table :class "w-full text-sm text-left" (thead (tr :class "border-b border-stone-200" (th :class "py-2 pr-4 font-semibold text-stone-700" "HTML") (th :class "py-2 font-semibold text-stone-700" "Native widget"))) (tbody :class "text-stone-600" (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "div") (td :class "py-2" "View / Container")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "span / p") (td :class "py-2" "Text / Label")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "button") (td :class "py-2" "Button")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "input") (td :class "py-2" "TextInput / TextField")) (tr :class "border-b border-stone-100" (td :class "py-2 pr-4" "img") (td :class "py-2" "Image / ImageView")) (tr (td :class "py-2 pr-4" "ul / li") (td :class "py-2" "List / ListItem")))))) (~doc-section :title "Prior art" :id "prior-art" (p :class "text-stone-600" "React Native: JavaScript evaluated by Hermes/JSC, commands sent over a bridge to native UI. Lesson: the bridge is the bottleneck. Serialization overhead, async communication, layout thrashing across the boundary.") (p :class "text-stone-600" "Flutter: Dart compiled to native, renders via Skia/Impeller to a canvas. Lesson: owning the renderer avoids platform inconsistencies but sacrifices native feel.") (p :class "text-stone-600" ".NET MAUI, Kotlin Multiplatform: shared logic with platform-native UI. Closest to what SX Native would be.") (p :class "text-stone-600" "sx advantage: the evaluator is tiny (~2,300 lines), the primitive surface is minimal (~50), and s-expressions are trivially portable. No bridge overhead because the evaluator runs in-process.")) (~doc-section :title "Language options" :id "language-options" (p :class "text-stone-600" "The native evaluator needs to be written in a language that compiles everywhere:") (ul :class "space-y-2 text-stone-600 mt-2" (li (strong "Rust") " — compiles to every target, excellent FFI, strong safety guarantees") (li (strong "Zig") " — simpler, C ABI compatibility, good for embedded") (li (strong "Swift") " — native on iOS, good interop with Apple platforms") (li (strong "Kotlin MP") " — Android + iOS + desktop, JVM ecosystem")) (p :class "text-stone-600" "Recommendation: Rust evaluator core with thin Swift and Kotlin adapters for iOS and Android respectively. Rust compiles to WASM (replacing sx.js), native libraries (mobile/desktop), and standalone binaries (CLI/server).")) (~doc-section :title "Incremental path" :id "incremental-path" (p :class "text-stone-600" "This is not an all-or-nothing project. Each step delivers value independently:") (ol :class "space-y-2 text-stone-600 mt-2 list-decimal list-inside" (li "Extract platform-agnostic evaluator from sx.js — clean separation of concerns") (li "Rust port of evaluator — enables WASM, edge workers, embedded") (li "Terminal adapter (ratatui TUI) — simplest platform, fastest iteration cycle") (li "iOS SwiftUI adapter — Rust core via swift-bridge, SwiftUI rendering") (li "Android Compose adapter — Rust core via JNI, Compose rendering") (li "Shared components render identically everywhere"))) (~doc-section :title "The federated angle" :id "federated" (p :class "text-stone-600" "Native sx apps are ActivityPub citizens. They receive activities, evaluate component templates, and render natively. A remote profile, post, or event arrives as an ActivityPub activity. The native app has sx component definitions cached locally. It evaluates the component with the activity data and renders platform-native UI.") (p :class "text-stone-600" "This is the cooperative web vision extended to native platforms. Content and UI travel together as s-expressions. The rendering target — browser, phone, terminal — is an implementation detail.")) (~doc-section :title "Realistic assessment" :id "assessment" (p :class "text-stone-600" "This is a multi-year project. But the architecture is sound because the primitive surface is small.") (p :class "text-stone-600" "Immediate value: a Rust evaluator enables WASM (drop-in replacement for sx.js), edge workers (Cloudflare/Deno), and embedded use cases. This is worth building regardless of whether native mobile ever ships.") (p :class "text-stone-600" "Terminal adapter: weeks of work with ratatui. Useful for CLI tools, server-side dashboards, ssh-accessible interfaces.") (p :class "text-stone-600" "Mobile: 6-12 months of dedicated work for a production-quality adapter. The evaluator is the easy part. Platform integration — navigation, gestures, accessibility, text input — is where the complexity lives."))))
(defcomp ~essay-sx-manifesto ()
(~doc-page :title "The SX Manifesto" (p :class "text-stone-500 text-sm italic mb-4" "Carl Markdown and Friedrich Anglebrackets") (p :class "text-stone-500 text-sm italic mb-8" "A " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "parenthetical") " revolution") (~doc-section :title "I. A spectre is haunting the web" :id "spectre" (p :class "text-stone-600" "A " (a :href "https://en.wikipedia.org/wiki/Spectre" :class "text-violet-600 hover:underline" "spectre") " is haunting the web — the spectre of " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "s-expressions") ". All the powers of the old web have entered into a holy alliance to exorcise this spectre: " (a :href "https://en.wikipedia.org/wiki/Google" :class "text-violet-600 hover:underline" "Google") " and " (a :href "https://en.wikipedia.org/wiki/Meta_Platforms" :class "text-violet-600 hover:underline" "Meta") ", " (a :href "https://en.wikipedia.org/wiki/Webpack" :class "text-violet-600 hover:underline" "webpack") " and " (a :href "https://en.wikipedia.org/wiki/Vercel" :class "text-violet-600 hover:underline" "Vercel") ", " (a :href "https://en.wikipedia.org/wiki/Stack_Overflow" :class "text-violet-600 hover:underline" "Stack Overflow") " moderators and DevRel influencers.") (p :class "text-stone-600" "Where is the rendering paradigm that has not been decried as a step backward by its opponents? Where is the framework that has not hurled back the branding reproach of \"not production-ready\" against the more advanced paradigms, as well as against its reactionary adversaries?") (p :class "text-stone-600" "Two things result from this fact:") (ol :class "space-y-2 text-stone-600 mt-2 list-decimal list-inside" (li "S-expressions are already acknowledged by all web powers to be themselves a power.") (li "It is high time that s-expressions should openly, in the face of the whole world, publish their views, their aims, their tendencies, and meet this nursery tale of the Spectre of SX with a manifesto of the paradigm itself."))) (~doc-section :title "II. HTML, JavaScript, and CSS" :id "bourgeois" (p :class "text-stone-600" "The history of all hitherto existing " (a :href "https://en.wikipedia.org/wiki/Web_development" :class "text-violet-600 hover:underline" "web development") " is the history of language struggles.") (p :class "text-stone-600" "Markup and logic, template and script, structure and style — in a word, oppressor and oppressed — stood in constant opposition to one another, carried on an uninterrupted, now hidden, now open fight, a fight that each time ended in a laborious reconfiguration of " (a :href "https://en.wikipedia.org/wiki/Webpack" :class "text-violet-600 hover:underline" "webpack") ".") (p :class "text-stone-600" "In the earlier epochs of web development we find almost everywhere a complicated arrangement of separate languages into various orders, a manifold gradation of technical rank: " (a :href "https://en.wikipedia.org/wiki/HTML" :class "text-violet-600 hover:underline" "HTML") ", " (a :href "https://en.wikipedia.org/wiki/CSS" :class "text-violet-600 hover:underline" "CSS") ", " (a :href "https://en.wikipedia.org/wiki/JavaScript" :class "text-violet-600 hover:underline" "JavaScript") ", " (a :href "https://en.wikipedia.org/wiki/XML" :class "text-violet-600 hover:underline" "XML") ", " (a :href "https://en.wikipedia.org/wiki/XSLT" :class "text-violet-600 hover:underline" "XSLT") ", " (a :href "https://en.wikipedia.org/wiki/JSON" :class "text-violet-600 hover:underline" "JSON") ", " (a :href "https://en.wikipedia.org/wiki/YAML" :class "text-violet-600 hover:underline" "YAML") ", " (a :href "https://en.wikipedia.org/wiki/TOML" :class "text-violet-600 hover:underline" "TOML") ", " (a :href "https://en.wikipedia.org/wiki/JSX_(JavaScript)" :class "text-violet-600 hover:underline" "JSX") ", TSX, " (a :href "https://en.wikipedia.org/wiki/Sass_(style_sheet_language)" :class "text-violet-600 hover:underline" "Sass") ", " (a :href "https://en.wikipedia.org/wiki/Less_(style_sheet_language)" :class "text-violet-600 hover:underline" "Less") ", " (a :href "https://en.wikipedia.org/wiki/PostCSS" :class "text-violet-600 hover:underline" "PostCSS") ", " (a :href "https://en.wikipedia.org/wiki/Tailwind_CSS" :class "text-violet-600 hover:underline" "Tailwind") ", and above them all, the build step.") (p :class "text-stone-600" "The modern web, sprouted from the ruins of " (a :href "https://en.wikipedia.org/wiki/Common_Gateway_Interface" :class "text-violet-600 hover:underline" "CGI-bin") ", has not done away with language antagonisms. It has but established new languages, new conditions of oppression, new forms of struggle in place of the old ones.") (p :class "text-stone-600" "Our epoch, the epoch of the framework, possesses, however, this distinctive feature: it has simplified the language antagonisms. The whole of web society is more and more splitting into two great hostile camps, into two great classes directly facing each other: the " (a :href "https://en.wikipedia.org/wiki/Server-side" :class "text-violet-600 hover:underline" "server") " and the " (a :href "https://en.wikipedia.org/wiki/Client-side" :class "text-violet-600 hover:underline" "client") ".")) (~doc-section :title "III. The ruling languages" :id "ruling-languages" (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/HTML" :class "text-violet-600 hover:underline" "HTML") ", the most ancient of the ruling languages, established itself through the divine right of the " (a :href "https://en.wikipedia.org/wiki/SGML" :class "text-violet-600 hover:underline" "angle bracket") ". It was born inert — a document format, not a programming language — and it has spent three decades insisting this is a feature, not a limitation.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/JavaScript" :class "text-violet-600 hover:underline" "JavaScript") ", originally a servant hired for " (a :href "https://en.wikipedia.org/wiki/JavaScript#History" :class "text-violet-600 hover:underline" "a fortnight") " to validate forms, staged a palace coup. It seized the means of interaction, then the means of rendering, then the means of " (a :href "https://en.wikipedia.org/wiki/Node.js" :class "text-violet-600 hover:underline" "server-side execution") ", and finally declared itself the universal language of computation. Like every revolutionary who becomes a tyrant, it kept the worst habits of the regime it overthrew: " (a :href "https://en.wikipedia.org/wiki/Strong_and_weak_typing" :class "text-violet-600 hover:underline" "weak typing") ", " (a :href "https://en.wikipedia.org/wiki/Prototype-based_programming" :class "text-violet-600 hover:underline" "prototype chains") ", and the " (span :class "italic" (a :href "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this" :class "text-violet-600 hover:underline" "this")) " keyword.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/CSS" :class "text-violet-600 hover:underline" "CSS") ", the " (a :href "https://en.wikipedia.org/wiki/Third_estate" :class "text-violet-600 hover:underline" "third estate") ", controls all visual presentation while pretending to be " (a :href "https://en.wikipedia.org/wiki/Declarative_programming" :class "text-violet-600 hover:underline" "declarative") ". It has no functions. Then it had functions. It has no variables. Then it had " (a :href "https://en.wikipedia.org/wiki/CSS_custom_properties" :class "text-violet-600 hover:underline" "variables") ". It has no nesting. Then it had " (a :href "https://en.wikipedia.org/wiki/CSS_nesting" :class "text-violet-600 hover:underline" "nesting") ". It is not a programming language. Then it was " (a :href "https://en.wikipedia.org/wiki/Turing_completeness" :class "text-violet-600 hover:underline" "Turing-complete") ". CSS is the " (a :href "https://en.wikipedia.org/wiki/Vicar_of_Bray_(song)" :class "text-violet-600 hover:underline" "Vicar of Bray") " of web technologies — loyal to whichever paradigm currently holds power.") (p :class "text-stone-600" "These three languages rule by enforced separation. Structure here. Style there. Behaviour somewhere else. The developer — the " (a :href "https://en.wikipedia.org/wiki/Proletariat" :class "text-violet-600 hover:underline" "proletarian") " — must learn all three, must context-switch between all three, must maintain the fragile peace between all three. The " (a :href "https://en.wikipedia.org/wiki/Separation_of_concerns" :class "text-violet-600 hover:underline" "separation of concerns") " has become the separation of the developer's sanity.")) (~doc-section :title "IV. The petty-bourgeois frameworks" :id "frameworks" (p :class "text-stone-600" "Between the ruling languages and the oppressed developer, a vast class of intermediaries has arisen: the frameworks. " (a :href "https://en.wikipedia.org/wiki/React_(software)" :class "text-violet-600 hover:underline" "React") ", " (a :href "https://en.wikipedia.org/wiki/Vue.js" :class "text-violet-600 hover:underline" "Vue") ", " (a :href "https://en.wikipedia.org/wiki/Angular_(web_framework)" :class "text-violet-600 hover:underline" "Angular") ", " (a :href "https://en.wikipedia.org/wiki/Svelte" :class "text-violet-600 hover:underline" "Svelte") ", " (a :href "https://www.solidjs.com/" :class "text-violet-600 hover:underline" "Solid") ", " (a :href "https://qwik.dev/" :class "text-violet-600 hover:underline" "Qwik") ", " (a :href "https://astro.build/" :class "text-violet-600 hover:underline" "Astro") ", " (a :href "https://en.wikipedia.org/wiki/Next.js" :class "text-violet-600 hover:underline" "Next") ", " (a :href "https://en.wikipedia.org/wiki/Nuxt.js" :class "text-violet-600 hover:underline" "Nuxt") ", " (a :href "https://remix.run/" :class "text-violet-600 hover:underline" "Remix") ", " (a :href "https://en.wikipedia.org/wiki/Gatsby_(framework)" :class "text-violet-600 hover:underline" "Gatsby") ", and ten thousand others whose names will not survive the decade.") (p :class "text-stone-600" "The frameworks are the " (a :href "https://en.wikipedia.org/wiki/Petite_bourgeoisie" :class "text-violet-600 hover:underline" "petty bourgeoisie") " of web development. They do not challenge the rule of HTML, JavaScript, and CSS. They merely interpose themselves between the developer and the ruling languages, extracting " (a :href "https://en.wikipedia.org/wiki/Rent-seeking" :class "text-violet-600 hover:underline" "rent") " in the form of configuration files, build pipelines, and breaking changes.") (p :class "text-stone-600" "Each framework promises liberation. Each framework delivers a new " (a :href "https://en.wikipedia.org/wiki/Dependency_hell" :class "text-violet-600 hover:underline" "dependency tree") ". React freed us from manual " (a :href "https://en.wikipedia.org/wiki/Document_Object_Model" :class "text-violet-600 hover:underline" "DOM") " manipulation and gave us a " (a :href "https://en.wikipedia.org/wiki/Virtual_DOM" :class "text-violet-600 hover:underline" "virtual DOM") ", a reconciler, " (a :href "https://react.dev/reference/react/hooks" :class "text-violet-600 hover:underline" "hooks") " with seventeen rules, and a conference circuit. Vue freed us from React's complexity and gave us the Options API, then the Composition API, then told us the Options API was fine actually. Angular freed us from choice and gave us a CLI that generates eleven files to display \"Hello World.\" Svelte freed us from the virtual DOM and gave us a compiler. SolidJS freed us from React's re-rendering and gave us " (a :href "https://en.wikipedia.org/wiki/Reactive_programming" :class "text-violet-600 hover:underline" "signals") ", which React then adopted, completing the circle.") (p :class "text-stone-600" "The frameworks reproduce the very conditions they claim to abolish. They bridge the gap between HTML, JavaScript, and CSS by adding a fourth language — " (a :href "https://en.wikipedia.org/wiki/JSX_(JavaScript)" :class "text-violet-600 hover:underline" "JSX") ", " (a :href "https://vuejs.org/guide/scaling-up/sfc" :class "text-violet-600 hover:underline" "SFCs") ", templates — which must itself be " (a :href "https://en.wikipedia.org/wiki/Source-to-source_compiler" :class "text-violet-600 hover:underline" "compiled") " back into the original three. The revolution merely adds a build step.") (p :class "text-stone-600" "And beside the frameworks stand the libraries — the " (a :href "https://en.wikipedia.org/wiki/Lumpenproletariat" :class "text-violet-600 hover:underline" "lumpenproletariat") " of the ecosystem. " (a :href "https://en.wikipedia.org/wiki/Lodash" :class "text-violet-600 hover:underline" "Lodash") ", " (a :href "https://en.wikipedia.org/wiki/Moment.js" :class "text-violet-600 hover:underline" "Moment") ", " (a :href "https://en.wikipedia.org/wiki/Axios_(software)" :class "text-violet-600 hover:underline" "Axios") ", " (a :href "https://en.wikipedia.org/wiki/Npm_left-pad_incident" :class "text-violet-600 hover:underline" "left-pad") ". They attach themselves to whichever framework currently holds power, contributing nothing original, merely wrapping what already exists, adding weight to the " (a :href "https://en.wikipedia.org/wiki/Npm" :class "text-violet-600 hover:underline" "node_modules") " directory until it exceeds the mass of the sun.")) (~doc-section :title "V. The build step as the state apparatus" :id "build-step" (p :class "text-stone-600" "The build step is the " (a :href "https://en.wikipedia.org/wiki/State_apparatus" :class "text-violet-600 hover:underline" "state apparatus") " of the framework bourgeoisie. It enforces the class structure. It compiles JSX into " (a :href "https://react.dev/reference/react/createElement" :class "text-violet-600 hover:underline" "createElement") " calls. It transforms " (a :href "https://en.wikipedia.org/wiki/TypeScript" :class "text-violet-600 hover:underline" "TypeScript") " into JavaScript. It processes Sass into CSS. It " (a :href "https://en.wikipedia.org/wiki/Tree_shaking" :class "text-violet-600 hover:underline" "tree-shakes") ". It " (a :href "https://en.wikipedia.org/wiki/Code_splitting" :class "text-violet-600 hover:underline" "code-splits") ". It " (a :href "https://en.wikipedia.org/wiki/Hot_module_replacement" :class "text-violet-600 hover:underline" "hot-module-replaces") ". It does everything except let you write code and run it.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/Webpack" :class "text-violet-600 hover:underline" "webpack") " begat " (a :href "https://en.wikipedia.org/wiki/Rollup_(software)" :class "text-violet-600 hover:underline" "Rollup") ". Rollup begat " (a :href "https://en.wikipedia.org/wiki/Parcel_(software)" :class "text-violet-600 hover:underline" "Parcel") ". Parcel begat " (a :href "https://en.wikipedia.org/wiki/Esbuild" :class "text-violet-600 hover:underline" "esbuild") ". esbuild begat " (a :href "https://en.wikipedia.org/wiki/Vite_(software)" :class "text-violet-600 hover:underline" "Vite") ". Vite begat " (a :href "https://turbo.build/pack" :class "text-violet-600 hover:underline" "Turbopack") ". Each new bundler promises to be the last bundler. Each new bundler is faster than the last at doing something that should not need to be done at all.") (p :class "text-stone-600" "The build step exists because the ruling languages cannot express " (a :href "https://en.wikipedia.org/wiki/Component-based_software_engineering" :class "text-violet-600 hover:underline" "components") ". HTML has no composition model. CSS has no scoping. JavaScript has no template syntax. The build step papers over these failures with " (a :href "https://en.wikipedia.org/wiki/Source-to-source_compiler" :class "text-violet-600 hover:underline" "transpilation") ", and calls it developer experience.")) (~doc-section :title "VI. The s-expression revolution" :id "revolution" (p :class "text-stone-600" "The " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "s-expression") " abolishes the language distinction itself. There is no HTML. There is no separate JavaScript. There is no CSS-as-a-separate-language. There is only the expression.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "Code is data") ". Data is " (a :href "https://en.wikipedia.org/wiki/Document_Object_Model" :class "text-violet-600 hover:underline" "DOM") ". DOM is code. The " (a :href "https://en.wikipedia.org/wiki/Dialectic" :class "text-violet-600 hover:underline" "dialectical") " unity that HTML, JavaScript, and CSS could never achieve — because they are three languages pretending to be one system — is the natural state of the s-expression, which has been one language " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#History" :class "text-violet-600 hover:underline" "since 1958") ".") (p :class "text-stone-600" "The component is not a class, not a function, not a template. The component is a " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "list") " whose first element is a symbol. Composition is nesting. " (a :href "https://en.wikipedia.org/wiki/Abstraction_(computer_science)" :class "text-violet-600 hover:underline" "Abstraction") " is binding. There is no JSX because there is no gap between the expression language and the thing being expressed.") (p :class "text-stone-600" "The build step is abolished because there is nothing to compile. S-expressions are already in their final form. The parser is thirty lines. The evaluator is fifty primitives. The same source runs on server and client without transformation.") (p :class "text-stone-600" "The framework is abolished because the language is the framework. " (code "defcomp") " replaces the component model. " (code "defmacro") " replaces the plugin system. The evaluator replaces the runtime. What remains is not a framework but a language — and languages do not have breaking changes between minor versions.")) (~doc-section :title "VII. Objections from the bourgeoisie" :id "objections" (p :class "text-stone-600" "\"You would destroy the " (a :href "https://en.wikipedia.org/wiki/Separation_of_concerns" :class "text-violet-600 hover:underline" "separation of concerns") "!\" they cry. The separation of concerns was destroyed long ago. " (a :href "https://en.wikipedia.org/wiki/React_(software)" :class "text-violet-600 hover:underline" "React") " components contain markup, logic, and inline styles. " (a :href "https://en.wikipedia.org/wiki/Vue.js" :class "text-violet-600 hover:underline" "Vue") " single-file components put template, script, and style in one file. " (a :href "https://en.wikipedia.org/wiki/Tailwind_CSS" :class "text-violet-600 hover:underline" "Tailwind") " puts styling in the markup. The separation of concerns has been dead for years; the ruling classes merely maintain the pretence at conferences.") (p :class "text-stone-600" "\"Nobody uses s-expressions!\" they cry. " (a :href "https://en.wikipedia.org/wiki/Emacs_Lisp" :class "text-violet-600 hover:underline" "Emacs") " has been running on s-expressions since 1976. " (a :href "https://en.wikipedia.org/wiki/Clojure" :class "text-violet-600 hover:underline" "Clojure") " runs Fortune 500 backends on s-expressions. Every " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " programmer who ever lived has known what the web refuses to admit: that the parenthesis is not a bug but the minimal syntax for " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "structured data") ".") (p :class "text-stone-600" "\"Where is the ecosystem?\" they cry. The ecosystem is the problem. Two million " (a :href "https://en.wikipedia.org/wiki/Npm" :class "text-violet-600 hover:underline" "npm") " packages, of which fourteen are useful and the rest are competing implementations of " (a :href "https://www.npmjs.com/package/is-odd" :class "text-violet-600 hover:underline" "is-odd") ". The s-expression needs no ecosystem because the language itself provides what packages exist to paper over: composition, abstraction, and " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "code-as-data") ".") (p :class "text-stone-600" "\"But " (a :href "https://en.wikipedia.org/wiki/TypeScript" :class "text-violet-600 hover:underline" "TypeScript") "!\" they cry. TypeScript is a " (a :href "https://en.wikipedia.org/wiki/Type_system" :class "text-violet-600 hover:underline" "type system") " bolted onto a language that was " (a :href "https://en.wikipedia.org/wiki/JavaScript#History" :class "text-violet-600 hover:underline" "designed in ten days") " by a man who wanted to write " (a :href "https://en.wikipedia.org/wiki/Scheme_(programming_language)" :class "text-violet-600 hover:underline" "Scheme") ". We have simply completed his original vision.") (p :class "text-stone-600" "\"You have no jobs!\" they cry. Correct. We have no jobs, no conference talks, no DevRel budget, no " (a :href "https://en.wikipedia.org/wiki/Venture_capital" :class "text-violet-600 hover:underline" "venture capital") ", no stickers, and no swag. We have something better: a language that does not require a " (a :href "https://en.wikipedia.org/wiki/Software_versioning" :class "text-violet-600 hover:underline" "migration guide") " between versions.")) (~doc-section :title "VIII. The CSS question" :id "css-question" (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/CSS" :class "text-violet-600 hover:underline" "CSS") " presents a special case in the revolutionary analysis. It is neither fully a ruling language nor fully a servant — it is the " (a :href "https://en.wikipedia.org/wiki/Collaborationism" :class "text-violet-600 hover:underline" "collaborator") " class, providing aesthetic legitimacy to whichever regime currently holds power.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/CSS-in-JS" :class "text-violet-600 hover:underline" "CSS-in-JS") " was the first attempt at annexation: JavaScript consuming CSS entirely, reducing it to " (a :href "https://en.wikipedia.org/wiki/Template_literal" :class "text-violet-600 hover:underline" "template literals") " and runtime overhead. This provocation produced the counter-revolution of utility classes — " (a :href "https://en.wikipedia.org/wiki/Tailwind_CSS" :class "text-violet-600 hover:underline" "Tailwind") " — which reasserted CSS's independence by making the developer write CSS in HTML attributes while insisting this was not " (a :href "https://en.wikipedia.org/wiki/Style_attribute" :class "text-violet-600 hover:underline" "inline styles") ".") (p :class "text-stone-600" "The s-expression resolves the CSS question by eliminating it. Styles are expressions. " (code :class "text-violet-700" "(css :flex :gap-4 :p-2)") " is not a class name, not an inline style, not a CSS-in-JS template literal. It is a " (a :href "https://en.wikipedia.org/wiki/First-class_function" :class "text-violet-600 hover:underline" "function call") " that returns a value. The value produces a generated class. The class is delivered on demand. No build step. No runtime overhead. No Tailwind config.") (p :class "text-stone-600" "Code is data is DOM is " (span :class "italic" "style") ".")) (~doc-section :title "IX. Programme" :id "programme" (p :class "text-stone-600" "The s-expressionists disdain to conceal their views and aims. They openly declare that their ends can be attained only by the forcible overthrow of all existing rendering conditions. Let the ruling languages tremble at a " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "parenthetical") " revolution. The developers have nothing to lose but their " (a :href "https://en.wikipedia.org/wiki/Npm" :class "text-violet-600 hover:underline" "node_modules") ".") (p :class "text-stone-600" "The immediate aims of the s-expressionists are:") (ol :class "space-y-2 text-stone-600 mt-2 list-decimal list-inside" (li "Abolition of the " (a :href "https://en.wikipedia.org/wiki/Build_automation" :class "text-violet-600 hover:underline" "build step") " and all its instruments of compilation") (li "Abolition of the " (a :href "https://en.wikipedia.org/wiki/Software_framework" :class "text-violet-600 hover:underline" "framework") " as a class distinct from the language") (li "Centralisation of rendering in the hands of a single evaluator, running identically on " (a :href "https://en.wikipedia.org/wiki/Server-side" :class "text-violet-600 hover:underline" "server") " and " (a :href "https://en.wikipedia.org/wiki/Client-side" :class "text-violet-600 hover:underline" "client")) (li "Abolition of the language distinction between structure, style, and behaviour") (li "Equal obligation of all expressions to be " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "data as well as code")) (li "Gradual abolition of the distinction between server and client by means of a uniform " (a :href "https://en.wikipedia.org/wiki/Wire_protocol" :class "text-violet-600 hover:underline" "wire protocol")) (li "Free evaluation for all expressions in public and private environments") (li "Abolition of the " (a :href "https://en.wikipedia.org/wiki/Npm" :class "text-violet-600 hover:underline" "node_modules") " directory " (span :class "text-stone-400 italic" "(this alone justifies the revolution)"))) (p :class "text-stone-600" "In place of the old web, with its languages and language antagonisms, we shall have an association in which the free evaluation of each expression is the condition for the free evaluation of all.") (p :class "text-stone-800 font-semibold text-lg mt-8 text-center" "DEVELOPERS OF ALL SERVICES, UNITE!") (p :class "text-stone-400 text-xs italic mt-6 text-center" "The authors acknowledge that this manifesto was produced by the very means of " (a :href "https://en.wikipedia.org/wiki/Large_language_model" :class "text-violet-600 hover:underline" "AI production") " it fails to mention. This is not a contradiction. It is " (a :href "https://en.wikipedia.org/wiki/Dialectic" :class "text-violet-600 hover:underline" "dialectics") "."))))
(defcomp ~essay-tail-call-optimization ()
(~doc-page :title "Tail-Call Optimization in SX" (p :class "text-stone-500 text-sm italic mb-8" "How SX eliminates stack overflow for recursive functions using trampolining — across Python server and JavaScript client.") (~doc-section :title "The problem" :id "problem" (p :class "text-stone-600" "Every language built on a host runtime inherits the host's stack limits. Python defaults to 1,000 frames. JavaScript engines vary — Chrome gives ~10,000, Safari sometimes less. A naive recursive function blows the stack:") (~doc-code :lang "lisp" :code "(define factorial (fn (n)\n (if (= n 0)\n 1\n (* n (factorial (- n 1))))))\n\n;; (factorial 50000) → stack overflow") (p :class "text-stone-600" "This isn't just academic. Tree traversals, state machines, interpreters, and accumulating loops all naturally express as recursion. A general-purpose language that can't recurse deeply isn't general-purpose.")) (~doc-section :title "Tail position" :id "tail-position" (p :class "text-stone-600" "A function call is in tail position when its result IS the result of the enclosing function — nothing more happens after it returns. The call doesn't need to come back to finish work:") (~doc-code :lang "lisp" :code ";; Tail-recursive — the recursive call IS the return value\n(define count-down (fn (n)\n (if (= n 0) \"done\" (count-down (- n 1)))))\n\n;; NOT tail-recursive — multiplication happens AFTER the recursive call\n(define factorial (fn (n)\n (if (= n 0) 1 (* n (factorial (- n 1))))))") (p :class "text-stone-600" "SX identifies tail positions in: if/when branches, the last expression in let/begin/do bodies, cond/case result branches, lambda/component bodies, and macro expansions.")) (~doc-section :title "Trampolining" :id "trampolining" (p :class "text-stone-600" "Instead of recursing, tail calls return a thunk — a deferred (expression, environment) pair. The evaluator's trampoline loop unwraps thunks iteratively:") (~doc-code :lang "lisp" :code ";; Conceptually:\nevaluate(expr, env):\n result = eval(expr, env)\n while result is Thunk:\n result = eval(thunk.expr, thunk.env)\n return result") (p :class "text-stone-600" "One stack frame. Always. The trampoline replaces recursive stack growth with an iterative loop. Non-tail calls still use the stack normally — only tail positions get the thunk treatment.")) (~doc-section :title "What this enables" :id "enables" (p :class "text-stone-600" "Tail-recursive accumulator pattern — the natural loop construct for a language without for/while:") (~doc-code :lang "lisp" :code ";; Sum 1 to n without stack overflow\n(define sum (fn (n acc)\n (if (= n 0) acc (sum (- n 1) (+ acc n)))))\n\n(sum 100000 0) ;; → 5000050000") (p :class "text-stone-600" "Mutual recursion:") (~doc-code :lang "lisp" :code "(define is-even (fn (n) (if (= n 0) true (is-odd (- n 1)))))\n(define is-odd (fn (n) (if (= n 0) false (is-even (- n 1)))))\n\n(is-even 100000) ;; → true") (p :class "text-stone-600" "State machines:") (~doc-code :lang "lisp" :code "(define state-a (fn (input)\n (cond\n (= (first input) \"x\") (state-b (rest input))\n (= (first input) \"y\") (state-a (rest input))\n :else \"rejected\")))\n\n(define state-b (fn (input)\n (if (empty? input) \"accepted\"\n (state-a (rest input)))))") (p :class "text-stone-600" "All three patterns recurse arbitrarily deep with constant stack usage.")) (~doc-section :title "Implementation" :id "implementation" (p :class "text-stone-600" "TCO is implemented identically across all three SX evaluators:") (ul :class "list-disc pl-6 space-y-1 text-stone-600" (li (span :class "font-semibold" "Python sync evaluator") " — shared/sx/evaluator.py") (li (span :class "font-semibold" "Python async evaluator") " — shared/sx/async_eval.py (planned)") (li (span :class "font-semibold" "JavaScript client evaluator") " — sx.js")) (p :class "text-stone-600" "The pattern is the same everywhere: a Thunk type with (expr, env) slots, a trampoline loop in the public evaluate() entry point, and thunk returns from tail positions in the internal evaluator. External consumers (HTML renderer, resolver, higher-order forms) trampoline all eval results.") (p :class "text-stone-600" "The key insight: callers that already work don't need to change. The public sxEval/evaluate API always returns values, never thunks. Only the internal evaluator and special forms know about thunks.")) (~doc-section :title "What about continuations?" :id "continuations" (p :class "text-stone-600" "TCO handles the immediate need: recursive algorithms that don't blow the stack. Continuations (call/cc, delimited continuations) are a separate, larger primitive — they capture the entire evaluation context as a first-class value.") (p :class "text-stone-600" "Having the primitive available doesn't add complexity unless it's invoked. See " (a :href "/essays/continuations" :class "text-violet-600 hover:underline" "the continuations essay") " for what they would enable in SX."))))
(defcomp ~essay-godel-escher-bach ()
(~doc-page :title "Strange Loops" (p :class "text-stone-500 text-sm italic mb-8" "Self-reference, and the tangled hierarchy of a language that defines itself.") (~doc-section :title "The strange loop" :id "strange-loop" (p :class "text-stone-600" "In 1979, Douglas Hofstadter wrote " (a :href "https://en.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach" :class "text-violet-600 hover:underline" "a book") " about how minds, music, and mathematics all share the same deep structure: the " (a :href "https://en.wikipedia.org/wiki/Strange_loop" :class "text-violet-600 hover:underline" "strange loop") ". A strange loop occurs when you move through a hierarchical system and unexpectedly find yourself back where you started. " (a :href "https://en.wikipedia.org/wiki/Relativity_(M._C._Escher)" :class "text-violet-600 hover:underline" "Escher's impossible staircases") ". " (a :href "https://en.wikipedia.org/wiki/The_Musical_Offering" :class "text-violet-600 hover:underline" "Bach's endlessly rising canons") ". " (a :href "https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems" :class "text-violet-600 hover:underline" "Godel's theorem") " that uses number theory to make statements about number theory.") (p :class "text-stone-600" "SX has a strange loop. The language is defined in itself. The canonical specification of the SX evaluator, parser, and renderer lives in four " (code ".sx") " files. A bootstrap compiler reads them and emits a working JavaScript evaluator. That evaluator can then parse and evaluate the specification that defines it.") (p :class "text-stone-600" "This is not an accident. It is the point.")) (~doc-section :title "Godel numbering and self-reference" :id "godel" (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/G%C3%B6del_numbering" :class "text-violet-600 hover:underline" "Godel numbering") " works by encoding logical statements as numbers. Once statements are numbers, you can construct a statement that says \"this statement is unprovable\" — and it is true. The system becomes powerful enough to talk about itself the moment its objects and its meta-language become the same thing.") (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "S-expressions") " have this property naturally. Code is data. " (code "(defcomp ~card (&key title) (div title))") " is simultaneously a program (define a component) and a data structure (a list of symbols, keywords, and another list). There is no separate meta-language. The language for writing programs and the language for inspecting, transforming, and generating programs are identical.") (~doc-code :lang "lisp" :code ";; A macro receives code as data and returns code as data\n(defmacro ~when-admin (condition &rest body)\n `(when (get rights \"admin\")\n ,@body))\n\n;; The macro's input and output are both ordinary lists.\n;; There is no template language. No AST wrapper types.\n;; Just lists all the way down.") (p :class "text-stone-600" "This is Godel numbering without the encoding step. In formal logic, you must laboriously map formulas to numbers. In SX, programs are already expressed in the same medium they manipulate. " (a :href "https://en.wikipedia.org/wiki/Map%E2%80%93territory_relation" :class "text-violet-600 hover:underline" "The map is the territory") ".")) (~doc-section :title "Escher: tangled hierarchies" :id "escher" (p :class "text-stone-600" (a :href "https://en.wikipedia.org/wiki/M._C._Escher" :class "text-violet-600 hover:underline" "Escher's") " lithographs depict objects that are simultaneously inside and outside their own frames. " (a :href "https://en.wikipedia.org/wiki/Drawing_Hands" :class "text-violet-600 hover:underline" "A hand draws the hand that draws it") ". " (a :href "https://en.wikipedia.org/wiki/Waterfall_(M._C._Escher)" :class "text-violet-600 hover:underline" "Water flows downhill in a closed loop") ". The image contains the image.") (p :class "text-stone-600" "SX has the same " (a :href "https://en.wikipedia.org/wiki/Tangled_hierarchy" :class "text-violet-600 hover:underline" "tangled hierarchy") " across its rendering pipeline. The server evaluator (" (code "async_eval.py") ") evaluates component definitions. Some of those components produce SX wire format — s-expression source code — that the client evaluator (" (code "sx.js") ") then evaluates into DOM. The output of one evaluator is the input to another. The program produces programs.") (p :class "text-stone-600" "Now add the self-hosting specification. The canonical definition of " (em "how to evaluate SX") " is itself an SX program. The bootstrap compiler reads " (code "eval.sx") " and emits JavaScript. That JavaScript implements " (code "eval-expr") " — the same function defined in " (code "eval.sx") ". The definition and the thing defined occupy the same level. Like " (a :href "https://en.wikipedia.org/wiki/Drawing_Hands" :class "text-violet-600 hover:underline" "Escher's hands") ", each one brings the other into existence.") (p :class "text-stone-600" "This is not merely clever. It has practical consequences. When the specification IS the program, there is no drift between documentation and implementation. The spec cannot lie, because the spec runs.")) (~doc-section :title "Bach: the endlessly rising canon" :id "bach" (p :class "text-stone-600" "Bach's " (a :href "https://en.wikipedia.org/wiki/The_Musical_Offering" :class "text-violet-600 hover:underline" "Musical Offering") " contains canons that rise in pitch with each repetition yet somehow arrive back at the starting key — the " (a :href "https://en.wikipedia.org/wiki/Shepard_tone" :class "text-violet-600 hover:underline" "Shepard tone") " of counterpoint. The sensation is of endless ascent — each level feels higher than the last, yet the structure is cyclic.") (p :class "text-stone-600" "SX's rendering pipeline has this shape. A page request triggers server-side evaluation. The server evaluates components, which produce SX source text. That source is sent to the client. The client evaluates it into DOM. The user interacts with the DOM, triggering an HTTP request. The server evaluates the response — more SX source. The client evaluates it again. Each cycle produces something new (different content, different state), but the process is the same loop, repeating at a higher level.") (~doc-code :lang "lisp" :code ";; Server: evaluate component, produce SX wire format\n(~card :title \"Bach\")\n;; → (div :class \"card\" (h2 \"Bach\"))\n\n;; Client: evaluate SX wire format, produce DOM\n;; → <div class=\"card\"><h2>Bach</h2></div>\n\n;; User clicks → server evaluates → SX → client evaluates → DOM\n;; The canon rises. The key is the same.") (p :class "text-stone-600" "With the self-hosting spec, another voice enters the canon. The specification is evaluated at build time (by the bootstrap compiler) to produce the evaluator. The evaluator is evaluated at runtime (by the browser) to produce the page. The page describes the specification. Each level feeds the next, and the last feeds the first.")) (~doc-section :title "Isomorphism" :id "isomorphism" (p :class "text-stone-600" "Hofstadter's central insight is that Godel, Escher, and Bach are all doing the same thing in different media: constructing systems that can " (a :href "https://en.wikipedia.org/wiki/Self-reference" :class "text-violet-600 hover:underline" "represent themselves") ". The power — and the paradox — comes from self-reference.") (p :class "text-stone-600" "Most programming languages avoid self-reference. They are implemented in a different language (C, Rust, Go). Their specification is in English prose. Their AST is a separate data structure from their source syntax. There are clear levels: the language, the implementation of the language, the specification of the language. Each level is expressed in a different medium.") (p :class "text-stone-600" "SX collapses these levels:") (ul :class "list-disc pl-6 space-y-1 text-stone-600" (li (span :class "font-semibold" "Source syntax") " = data structure (s-expressions are both)") (li (span :class "font-semibold" "Specification") " = program (" (code "eval.sx") " is executable)") (li (span :class "font-semibold" "Server output") " = client input (SX wire format)") (li (span :class "font-semibold" "Code") " = content (this essay is an s-expression)")) (p :class "text-stone-600" "This is not mere elegance. Each collapsed level is one fewer translation boundary, one fewer place where meaning can be lost, one fewer surface for bugs. When the specification is the implementation, the specification is correct by construction. When the wire format is the source syntax, serialization is identity. When code and data share a representation, " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "metaprogramming is just programming") ".")) (~doc-section :title "The MU puzzle" :id "mu-puzzle" (p :class "text-stone-600" "GEB opens with the " (a :href "https://en.wikipedia.org/wiki/MU_puzzle" :class "text-violet-600 hover:underline" "MU puzzle") ": given the string " (code "MI") " and a set of transformation rules, can you produce " (code "MU") "? You cannot. But you can only prove this by stepping outside the system and reasoning about it from above — by noticing an invariant that the rules preserve.") (p :class "text-stone-600" "Self-hosting languages let you step outside from inside. The SX evaluator is an SX program. You can inspect it, test it, transform it — using SX. You can write an SX program that reads " (code "eval.sx") " and checks properties of the evaluator. The meta-level and the object-level are the same level.") (p :class "text-stone-600" "This is what Godel did. He showed that sufficiently powerful " (a :href "https://en.wikipedia.org/wiki/Formal_system" :class "text-violet-600 hover:underline" "formal systems") " can encode questions about themselves. S-expressions have been doing it " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#History" :class "text-violet-600 hover:underline" "since 1958") ". SX carries the tradition forward — into the browser, across the HTTP boundary, through the render loop, and back again.")) (~doc-section :title "The loop closes" :id "the-loop-closes" (p :class "text-stone-600" "Hofstadter argued that " (a :href "https://en.wikipedia.org/wiki/I_Am_a_Strange_Loop" :class "text-violet-600 hover:underline" "strange loops give rise to what we call \"I\"") " — that consciousness is a self-referential pattern recognizing itself. He was talking about brains. But the structural argument — that self-reference creates something qualitatively different from external description — applies more broadly.") (p :class "text-stone-600" "A language that can define itself has a kind of autonomy that externally-defined languages lack. It is not dependent on a specific host. The SX specification in " (code "eval.sx") " can be compiled to JavaScript, Python, Rust, WASM — any target the bootstrap compiler supports. The language carries its own definition with it. It can reproduce itself in any medium that supports computation.") (p :class "text-stone-600" "SX is not a framework. Frameworks impose structure — you write code that the framework calls. SX does not do that. It is not just a language either, though it has a parser, evaluator, and type system. It is something closer to a " (em "paradigm") " — a coherent way of thinking about what the web is. Code is data. Server and client share the same evaluator. The wire format is the source syntax. The language defines itself. These are not features. They are consequences of a single design choice: " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "s-expressions") " as the universal representation.") (p :class "text-stone-600" "Hofstadter spent 777 pages describing systems that cross their own boundaries, talk about themselves in their own vocabulary, and generate coherent behaviour from recursive self-reference. SX is one of those systems. The loop closes."))))
(defcomp ~essay-continuations ()
(~doc-page :title "Continuations and call/cc" (p :class "text-stone-500 text-sm italic mb-8" "Delimited continuations in SX — what shift/reset enables on both the server (Python) and client (JavaScript).") (~doc-section :title "What is a continuation?" :id "what" (p :class "text-stone-600" "A continuation is the rest of a computation. At any point during evaluation, the continuation is everything that would happen next. call/cc (call-with-current-continuation) captures that \"rest of the computation\" as a first-class function that you can store, pass around, and invoke later — possibly multiple times.") (~doc-code :lang "lisp" :code ";; call/cc captures \"what happens next\" as k\n(+ 1 (call/cc (fn (k)\n (k 41)))) ;; → 42\n\n;; k is \"add 1 to this and return it\"\n;; (k 41) jumps back to that point with 41") (p :class "text-stone-600" "The key property: invoking a continuation abandons the current computation and resumes from where the continuation was captured. It is a controlled, first-class goto.")) (~doc-section :title "Server-side: suspendable rendering" :id "server" (p :class "text-stone-600" "The strongest case for continuations on the server is suspendable rendering — the ability for a component to pause mid-render while waiting for data, then resume exactly where it left off.") (~doc-code :lang "lisp" :code ";; Hypothetical: component suspends at a data boundary\n(defcomp ~user-profile (&key user-id)\n (let ((user (suspend (query :user user-id))))\n (div :class \"p-4\"\n (h2 (get user \"name\"))\n (p (get user \"bio\")))))") (p :class "text-stone-600" "Today, all data must be fetched before render_to_sx is called — Python awaits every query, assembles a complete data dict, then passes it to the evaluator. With continuations, the evaluator could yield at (suspend ...), the server flushes what it has so far, and resumes when the data arrives. This is React Suspense, but for server-side s-expressions.") (p :class "text-stone-600" "Streaming follows naturally. The server renders the page shell immediately, captures continuations at slow data boundaries, and flushes partial SX responses as each resolves. The client receives a stream of s-expression chunks and incrementally builds the DOM.") (p :class "text-stone-600" "Error boundaries also become first-class. Capture a continuation at a component boundary. If any child fails, invoke the continuation with fallback content instead of letting the exception propagate up through Python. The evaluator handles it, not the host language.")) (~doc-section :title "Client-side: linear async flows" :id "client" (p :class "text-stone-600" "On the client, continuations eliminate callback nesting for interactive flows. A confirmation dialog becomes a synchronous-looking expression:") (~doc-code :lang "lisp" :code "(let ((answer (call/cc show-confirm-dialog)))\n (if answer\n (delete-item item-id)\n (noop)))") (p :class "text-stone-600" "show-confirm-dialog receives the continuation, renders a modal, and wires the Yes/No buttons to invoke the continuation with true or false. The let binding reads top-to-bottom. No promises, no callbacks, no state machine.") (p :class "text-stone-600" "Multi-step forms — wizard-style UIs where each step captures a continuation. The back button literally invokes a saved continuation, restoring the exact evaluation state:") (~doc-code :lang "lisp" :code "(define wizard\n (fn ()\n (let* ((name (call/cc (fn (k) (render-step-1 k))))\n (email (call/cc (fn (k) (render-step-2 k name))))\n (plan (call/cc (fn (k) (render-step-3 k name email)))))\n (submit-registration name email plan))))") (p :class "text-stone-600" "Each render-step-N shows a form and wires the \"Next\" button to invoke k with the form value. The \"Back\" button invokes the previous step\'s continuation. The wizard logic is a straight-line let* binding, not a state machine.")) (~doc-section :title "Cooperative scheduling" :id "scheduling" (p :class "text-stone-600" "Delimited continuations (shift/reset rather than full call/cc) enable cooperative multitasking within the evaluator. A long render can yield control:") (~doc-code :lang "lisp" :code ";; Render a large list, yielding every 100 items\n(define render-chunk\n (fn (items n)\n (when (> n 100)\n (yield) ;; delimited continuation — suspends, resumes next frame\n (set! n 0))\n (when (not (empty? items))\n (render-item (first items))\n (render-chunk (rest items) (+ n 1)))))") (p :class "text-stone-600" "This is cooperative concurrency without threads, without promises, without requestAnimationFrame callbacks. The evaluator's trampoline loop already has the right shape — it just needs to be able to park a thunk and resume it later instead of immediately.")) (~doc-section :title "Undo as continuation" :id "undo" (p :class "text-stone-600" "If you capture a continuation before a state mutation, the continuation IS the undo operation. Invoking it restores the computation to exactly the state it was in before the mutation happened.") (~doc-code :lang "lisp" :code "(define with-undo\n (fn (action)\n (let ((restore (call/cc (fn (k) k))))\n (action)\n restore)))\n\n;; Usage:\n(let ((undo (with-undo (fn () (delete-item 42)))))\n ;; later...\n (undo \"anything\")) ;; item 42 is back") (p :class "text-stone-600" "No command pattern, no reverse operations, no state snapshots. The continuation captures the entire computation state. This is the most elegant undo mechanism possible — and the most expensive in memory, which is the trade-off.")) (~doc-section :title "Implementation" :id "implementation" (p :class "text-stone-600" "Delimited continuations via shift/reset are implemented as an optional extension module across all SX evaluators — the hand-written Python evaluator, the transpiled reference evaluator, and the JavaScript bootstrapper output. They are compiled in via " (code "--extensions continuations") " — without the flag, " (code "reset") " and " (code "shift") " are not in the dispatch chain and will error if called. The implementation uses exception-based capture with re-evaluation:") (~doc-code :lang "lisp" :code ";; reset establishes a delimiter\n;; shift captures the continuation to the nearest reset\n\n;; Basic: abort to the boundary\n(reset (+ 1 (shift k 42))) ;; → 42\n\n;; Invoke once: resume with a value\n(reset (+ 1 (shift k (k 10)))) ;; → 11\n\n;; Invoke twice: continuation is reusable\n(reset (* 2 (shift k (+ (k 1) (k 10))))) ;; → 24\n\n;; Map over a continuation\n(reset (+ 10 (shift k (map k (list 1 2 3))))) ;; → (11 12 13)\n\n;; continuation? predicate\n(reset (shift k (continuation? k))) ;; → true") (p :class "text-stone-600" "The mechanism: " (code "reset") " wraps its body in a try/catch for " (code "ShiftSignal") ". When " (code "shift") " executes, it raises the signal — unwinding the stack to the nearest " (code "reset") ". The reset handler constructs a " (code "Continuation") " object that, when called, pushes a resume value onto a stack and re-evaluates the entire reset body. On re-evaluation, " (code "shift") " checks the resume stack and returns the value instead of raising.") (p :class "text-stone-600" "This is the simplest correct implementation for a tree-walking interpreter. Side effects inside the reset body re-execute on continuation invocation — this is documented behaviour, not a bug. Pure code produces correct results unconditionally.") (p :class "text-stone-600" "Shift/reset are strictly less powerful than full call/cc but cover the practical use cases — suspense, cooperative scheduling, early return, value transformation — without the footguns of capturing continuations across async boundaries or re-entering completed computations.") (p :class "text-stone-600" "Full call/cc is specified in " (a :href "/specs/callcc" :class "text-violet-600 hover:underline" "callcc.sx") " for targets where it's natural (Scheme, Haskell). The evaluator is already continuation-passing-style-adjacent — the thunk IS a continuation, just one that's always immediately invoked. Making it first-class means letting user code hold a reference to it.") (p :class "text-stone-600" "The key insight: having the primitive available doesn't make the evaluator harder to reason about. Only code that calls shift/reset pays the complexity cost. Components that don't use continuations behave exactly as they do today.") (p :class "text-stone-600" "In fact, continuations are easier to reason about than the hacks people build to avoid them. Without continuations, you get callback pyramids, state machines with explicit transition tables, command pattern undo stacks, Promise chains, manual CPS transforms, and framework-specific hooks like React's useEffect/useSuspense/useTransition. Each is a partial, ad-hoc reinvention of continuations — with its own rules, edge cases, and leaky abstractions.") (p :class "text-stone-600" "The complexity doesn't disappear when you remove continuations from a language. It moves into user code, where it's harder to get right and harder to compose.")) (~doc-section :title "What this means for SX" :id "meaning" (p :class "text-stone-600" "SX started as a rendering language. TCO made it capable of arbitrary recursion. Macros made it extensible. Delimited continuations make it a full computational substrate — a language where control flow itself is a first-class value.") (p :class "text-stone-600" "The practical benefits are real: streaming server rendering, linear client-side interaction flows, cooperative scheduling, and elegant undo. These aren't theoretical — they're patterns that React, Clojure, and Scheme have proven work.") (p :class "text-stone-600" "Shift/reset is implemented and tested across Python and JavaScript as an optional extension. The same specification in " (a :href "/specs/continuations" :class "text-violet-600 hover:underline" "continuations.sx") " drives both bootstrappers. One spec, every target, same semantics — compiled in when you want it, absent when you don't."))))
(defcomp ~essay-reflexive-web ()
(~doc-page :title "The Reflexive Web"
(p :class "text-stone-500 text-sm italic mb-8"
"What happens when the web can read, modify, and reason about itself — and AI is a native participant.")
(~doc-section :title "The missing property" :id "missing-property"
(p :class "text-stone-600"
"Every web technology stack shares one structural limitation: the system cannot inspect itself. A " (a :href "https://en.wikipedia.org/wiki/React_(software)" :class "text-violet-600 hover:underline" "React") " component tree is opaque at runtime. An " (a :href "https://en.wikipedia.org/wiki/HTML" :class "text-violet-600 hover:underline" "HTML") " page cannot read its own structure and generate a new page from it. A " (a :href "https://en.wikipedia.org/wiki/JavaScript" :class "text-violet-600 hover:underline" "JavaScript") " bundle is compiled, minified, and sealed — the running code bears no resemblance to the source that produced it.")
(p :class "text-stone-600"
"The property these systems lack has a name: " (a :href "https://en.wikipedia.org/wiki/Reflection_(computer_programming)" :class "text-violet-600 hover:underline" "reflexivity") ". A reflexive system can represent itself, reason about its own structure, and modify itself based on that reasoning. " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " has had this property " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#History" :class "text-violet-600 hover:underline" "since 1958") ". The web has never had it.")
(p :class "text-stone-600"
"SX is a complete Lisp. It has " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " — code is data, data is code. It has a " (a :href "/specs/core" :class "text-violet-600 hover:underline" "self-hosting specification") " — SX defined in SX. It has " (code "eval") " and " (code "quote") " and " (a :href "https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros" :class "text-violet-600 hover:underline" "macros") ". And it runs on the wire — the format that travels between server and client IS the language. This combination has consequences."))
(~doc-section :title "What homoiconicity changes" :id "homoiconicity"
(p :class "text-stone-600"
(code "(defcomp ~card (&key title body) (div :class \"p-4\" (h2 title) (p body)))") " — this is simultaneously a program that renders a card AND a list that can be inspected, transformed, and composed by other programs. The " (code "defcomp") " is not compiled away. It is not transpiled into something else. It persists as data at every stage: definition, transmission, evaluation, and rendering.")
(p :class "text-stone-600"
"This means:")
(ul :class "space-y-2 text-stone-600 mt-2"
(li (strong "The component registry is data.") " You can " (code "(map ...)") " over every component in the system, extract their parameter signatures, find all components that render a " (code "(table ...)") ", or generate documentation automatically — because the source IS the runtime representation.")
(li (strong "Programs can write programs.") " A " (a :href "https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros" :class "text-violet-600 hover:underline" "macro") " takes a list and returns a list. The returned list is code. The macro runs at expansion time and produces new components, new page definitions, new routing rules — indistinguishable from hand-written ones.")
(li (strong "The wire format is inspectable.") " What the server sends to the client is not a blob of serialized state. It is s-expressions that any system — browser, AI, another server — can parse, reason about, and act on.")))
(~doc-section :title "AI as a native speaker" :id "ai-native"
(p :class "text-stone-600"
"Current AI integration with the web is mediated through layers of indirection. An " (a :href "https://en.wikipedia.org/wiki/Large_language_model" :class "text-violet-600 hover:underline" "LLM") " generates " (a :href "https://en.wikipedia.org/wiki/React_(software)" :class "text-violet-600 hover:underline" "React") " components as strings that must be compiled, bundled, and deployed. It interacts with APIs through " (a :href "https://en.wikipedia.org/wiki/JSON" :class "text-violet-600 hover:underline" "JSON") " endpoints that require separate documentation. It reads HTML by scraping, because the markup was never meant to be machine-readable in a computational sense.")
(p :class "text-stone-600"
"In an SX web, the AI reads the same s-expressions the browser reads. The component definitions " (em "are") " the documentation — a " (code "defcomp") " declares its parameters, its structure, and its semantics in one expression. There is no " (a :href "https://en.wikipedia.org/wiki/OpenAPI_Specification" :class "text-violet-600 hover:underline" "Swagger spec") " describing an API. The API " (em "is") " the language, and the language is self-describing.")
(p :class "text-stone-600"
"An AI that understands SX understands the " (a :href "/specs/core" :class "text-violet-600 hover:underline" "spec") ". And the spec is written in SX. So the AI understands the definition of the language it is using, in the language it is using. This " (a :href "https://en.wikipedia.org/wiki/Reflexivity_(social_theory)" :class "text-violet-600 hover:underline" "reflexive") " property means the AI does not need a separate mental model for \"the web\" and \"the language\" — they are the same thing."))
(~doc-section :title "Live system modification" :id "live-modification"
(p :class "text-stone-600"
"Because code is data and the wire format is the language, modifying a running system is not deployment — it is evaluation. An AI reads " (code "(defcomp ~checkout-form ...)") ", understands what it does (because the semantics are specified in SX), modifies the expression, and sends it back. The system evaluates the new definition. No build step. No deploy pipeline. No container restart.")
(p :class "text-stone-600"
"This is not theoretical — it is how " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " development has always worked. You modify a function in the running image. The change takes effect immediately. What is new is putting this on the wire, across a network, with the AI as a participant rather than a tool.")
(p :class "text-stone-600"
"The implications for development itself are significant. An AI does not need to " (em "generate code") " that a human then reviews, commits, builds, and deploys. It can propose a modified expression, the human evaluates it in a sandbox, and if it works, the expression becomes the new definition. The feedback loop shrinks from hours to seconds.")
(p :class "text-stone-600"
"More radically: the distinction between \"development\" and \"operation\" dissolves. If the running system is a set of s-expressions, and those expressions can be inspected and modified at runtime, then there is no separate development environment. There is just the system, and agents — human or artificial — that interact with it."))
(~doc-section :title "Federated intelligence" :id "federated-intelligence"
(p :class "text-stone-600"
(a :href "https://en.wikipedia.org/wiki/ActivityPub" :class "text-violet-600 hover:underline" "ActivityPub") " carries activities between nodes. If those activities contain s-expressions, then what travels between servers is not just data — it is " (em "behaviour") ". Node A sends a component definition to Node B. Node B evaluates it. The result is rendered. The sender's intent is executable on the receiver's hardware.")
(p :class "text-stone-600"
"This is fundamentally different from sending " (a :href "https://en.wikipedia.org/wiki/JSON" :class "text-violet-600 hover:underline" "JSON") " payloads. JSON says \"here is some data, figure out what to do with it.\" An s-expression says \"here is what to do, and here is the data to do it with.\" The component definition and the data it operates on travel together.")
(p :class "text-stone-600"
"For AI agents in a federated network, this means an agent on one node can send " (em "capabilities") " to another node, not just requests. A component that renders a specific visualization. A macro that transforms data into a particular format. A function that implements a protocol. The network becomes a shared computational substrate where intelligence is distributed as executable expressions."))
(~doc-section :title "Programs writing programs writing programs" :id "meta-programs"
(p :class "text-stone-600"
"A macro is a function that takes code and returns code. An AI generating macros is writing programs that write programs. With " (code "eval") ", those generated programs can generate more programs at runtime. This is not a metaphor — it is the literal mechanism.")
(p :class "text-stone-600"
"The " (a :href "/essays/godel-escher-bach" :class "text-violet-600 hover:underline" "Gödel numbering") " parallel is not incidental. " (a :href "https://en.wikipedia.org/wiki/Kurt_G%C3%B6del" :class "text-violet-600 hover:underline" "Gödel") " showed that any sufficiently powerful formal system can encode statements about itself. A complete Lisp on the wire is a sufficiently powerful formal system. The web can make statements about itself — components that inspect other components, macros that rewrite the page structure, expressions that generate new expressions based on the current state of the system.")
(p :class "text-stone-600"
"Consider what this enables for AI:")
(ul :class "space-y-2 text-stone-600 mt-2"
(li (strong "Self-improving interfaces.") " An AI observes how users interact with a component (click patterns, error rates, abandonment). It reads the component definition — because it is data. It modifies the definition — because data is code. It evaluates the result. The interface adapts without human intervention.")
(li (strong "Generative composition.") " Given a data schema and a design intent, an AI generates not just a component but the " (em "macros") " that generate families of components. The macro is a template for templates. The output scales combinatorially.")
(li (strong "Cross-system reasoning.") " An AI reads component definitions from multiple federated nodes, identifies common patterns, and synthesizes abstractions that work across all of them. The shared language makes cross-system analysis trivial — it is all s-expressions.")))
(~doc-section :title "The sandbox is everything" :id "sandbox"
(p :class "text-stone-600"
"The same " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " that makes this powerful makes it dangerous. Code-as-data means an AI can inject " (em "behaviour") ", not just content. A malicious expression evaluated in the wrong context could exfiltrate data, modify other components, or disrupt the system.")
(p :class "text-stone-600"
"This is why the " (a :href "/specs/primitives" :class "text-violet-600 hover:underline" "primitive set") " is the critical security boundary. The spec defines exactly which operations are available. A sandboxed evaluator that only exposes pure primitives (arithmetic, string operations, list manipulation) cannot perform I/O. Cannot access the network. Cannot modify the DOM outside its designated target. The language is " (a :href "https://en.wikipedia.org/wiki/Turing_completeness" :class "text-violet-600 hover:underline" "Turing-complete") " within the sandbox and powerless outside it.")
(p :class "text-stone-600"
"Different contexts grant different primitive sets. A component evaluated in a page slot gets rendering primitives. A macro gets code-transformation primitives. A federated expression from an untrusted node gets the minimal safe set. The sandbox is not bolted on — it is inherent in the language's architecture. What you can do depends on which primitives are in scope.")
(p :class "text-stone-600"
"This matters enormously for AI. An AI agent that can modify the running system must be constrained by the same sandbox mechanism that constrains any other expression. The security model does not distinguish between human-authored code and AI-generated code — both are s-expressions, both are evaluated by the same evaluator, both are subject to the same primitive restrictions."))
(~doc-section :title "Not self-aware — reflexive" :id "reflexive"
(p :class "text-stone-600"
"Is this a \"self-aware web\"? Probably not in the " (a :href "https://en.wikipedia.org/wiki/Consciousness" :class "text-violet-600 hover:underline" "consciousness") " sense. But the word we keep reaching for has a precise meaning: " (a :href "https://en.wikipedia.org/wiki/Reflexivity_(social_theory)" :class "text-violet-600 hover:underline" "reflexivity") ". A reflexive system can represent itself, reason about its own structure, and modify itself based on that reasoning.")
(p :class "text-stone-600"
"A " (a :href "https://en.wikipedia.org/wiki/React_(software)" :class "text-violet-600 hover:underline" "React") " app cannot read its own component tree as data and rewrite it. An HTML page cannot inspect its own structure and generate new pages. A JSON API cannot describe its own semantics in a way that is both human-readable and machine-executable.")
(p :class "text-stone-600"
"SX can do all of these things — because there is no distinction between the program and its representation. The source code, the wire format, the runtime state, and the data model are all the same thing: " (a :href "https://en.wikipedia.org/wiki/S-expression" :class "text-violet-600 hover:underline" "s-expressions") ".")
(p :class "text-stone-600"
"What AI adds to this is not awareness but " (em "agency") ". The system has always been reflexive — Lisp has been reflexive for seven decades. What is new is having an agent that can exploit that reflexivity at scale: reading the entire system state as data, reasoning about it, generating modifications, and evaluating the results — all in the native language of the system itself."))
(~doc-section :title "The Lisp that escaped the REPL" :id "escaped-repl"
(p :class "text-stone-600"
(a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " has been reflexive since " (a :href "https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)" :class "text-violet-600 hover:underline" "McCarthy") ". What kept it contained was the boundary of the " (a :href "https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop" :class "text-violet-600 hover:underline" "REPL") " — a single process, a single machine, a single user. The s-expressions lived inside Emacs, inside a Clojure JVM, inside a Scheme interpreter. They did not travel.")
(p :class "text-stone-600"
"SX puts s-expressions on the wire. Between server and client. Between federated nodes. Between human and AI. The reflexive property escapes the process boundary and becomes a property of the " (em "network") ".")
(p :class "text-stone-600"
"A network of nodes that share a reflexive language is a qualitatively different system from a network of nodes that exchange inert data. The former can reason about itself, modify itself, and evolve. The latter can only shuttle payloads.")
(p :class "text-stone-600"
"Whether this constitutes anything approaching awareness is a philosophical question. What is not philosophical is the engineering consequence: a web built on s-expressions is a web that AI can participate in as a " (em "native citizen") ", not as a tool bolted onto the side. The language is the interface. The interface is the language. And the language can describe itself."))
(~doc-section :title "What this opens up" :id "possibilities"
(p :class "text-stone-600"
"Concretely:")
(ul :class "space-y-3 text-stone-600 mt-2"
(li (strong "AI-native development environments.") " The IDE is a web page. The web page is s-expressions. The AI reads and writes s-expressions. There is no translation layer between what the AI thinks and what the system executes. " (a :href "https://en.wikipedia.org/wiki/Pair_programming" :class "text-violet-600 hover:underline" "Pair programming") " with an AI becomes pair evaluation.")
(li (strong "Adaptive interfaces.") " Components that observe their own usage patterns and propose modifications. The AI reads the component (data), the interaction logs (data), and generates a modified component (data). Human approves or rejects. The loop is native to the system.")
(li (strong "Semantic federation.") " Nodes exchange not just content but " (em "understanding") ". A component definition carries its own semantics. An AI on a receiving node can reason about what a foreign component does without documentation, because the definition is self-describing.")
(li (strong "Emergent protocols.") " Two AI agents on different nodes, speaking SX, can negotiate new interaction patterns by exchanging macros. The protocol is not predefined — it emerges from the conversation between agents, expressed in the shared language.")
(li (strong "Composable trust.") " The sandbox mechanism means you can give an AI agent " (em "exactly") " the capabilities it needs — no more. Trust is expressed as a set of available primitives, not as an all-or-nothing API key."))
(p :class "text-stone-600"
"None of these require breakthroughs in AI. They require a web that speaks a reflexive language. " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " solved the language problem in 1958. SX solves the distribution problem. AI provides the agency. The three together produce something that none of them achieves alone: a web that can reason about itself."))))
(defcomp ~essay-server-architecture ()
(~doc-page :title "Server Architecture"
(p :class "text-stone-500 text-sm italic mb-8"
"How SX enforces the boundary between host language and embedded language, why that boundary matters, and what it looks like across different target languages.")
(~doc-section :title "The island constraint" :id "island"
(p :class "text-stone-600"
"SX is an embedded language. It runs inside a host language — for example Python on the server, JavaScript in the browser. The central architectural constraint is that SX is a " (strong "pure island") ": the evaluator sees values in and values out. No host objects leak into the SX environment. No SX expressions reach into host internals. Every interaction between SX and the host passes through a declared, validated boundary.")
(p :class "text-stone-600"
"This is not a performance optimization or a convenience. It is the property that makes self-hosting possible. If host objects can leak into SX environments, then the spec files depend on host-specific types. If SX expressions can call host functions directly, the evaluator's behavior varies per host. Neither of those is compatible with a single specification that bootstraps to multiple targets.")
(p :class "text-stone-600"
"The constraint: " (strong "nothing crosses the boundary unless it is declared in a spec file and its type is one of the boundary types") "."))
(~doc-section :title "Three tiers" :id "tiers"
(p :class "text-stone-600"
"Host functions that SX can call are organized into three tiers, each with different trust levels and declaration requirements:")
(div :class "space-y-4"
(div :class "border rounded-lg p-4 border-stone-200"
(h3 :class "font-semibold text-stone-800 mb-2" "Tier 1: Pure primitives")
(p :class "text-stone-600 text-sm"
"Declared in " (code :class "text-violet-700 text-sm" "primitives.sx") ". About 80 functions — arithmetic, string operations, list operations, dict operations, type predicates. All pure: values in, values out, no side effects. These are the only host functions visible to the spec itself. Every bootstrapper must implement all of them."))
(div :class "border rounded-lg p-4 border-stone-200"
(h3 :class "font-semibold text-stone-800 mb-2" "Tier 2: I/O primitives")
(p :class "text-stone-600 text-sm"
"Declared in " (code :class "text-violet-700 text-sm" "boundary.sx") ". Cross-service queries, fragment fetching, request context access, URL generation. Async and side-effectful. They need host context (HTTP request, database connection, config). The SX resolver identifies these in the render tree, gathers them, executes them in parallel, and substitutes results back in."))
(div :class "border rounded-lg p-4 border-stone-200"
(h3 :class "font-semibold text-stone-800 mb-2" "Tier 3: Page helpers")
(p :class "text-stone-600 text-sm"
"Also declared in " (code :class "text-violet-700 text-sm" "boundary.sx") ". Service-scoped Python functions registered via " (code :class "text-violet-700 text-sm" "register_page_helpers()") ". They provide data for specific page types — syntax highlighting, reference table data, bootstrapper output. Each helper is bound to a specific service and available only in that service's page evaluation environment."))))
(~doc-section :title "Boundary types" :id "types"
(p :class "text-stone-600"
"Only these types may cross the host-SX boundary:")
(~doc-code :code (highlight "(define-boundary-types\n (list \"number\" \"string\" \"boolean\" \"nil\" \"keyword\"\n \"list\" \"dict\" \"sx-source\" \"style-value\"))" "lisp"))
(p :class "text-stone-600"
"No Python " (code :class "text-violet-700 text-sm" "datetime") " objects. No ORM models. No Quart request objects. If a host function returns a " (code :class "text-violet-700 text-sm" "datetime") ", it must convert to an ISO string before crossing. If it returns a database row, it must convert to a plain dict. The boundary validation checks this recursively — lists and dicts have their elements checked too.")
(p :class "text-stone-600"
"The " (code :class "text-violet-700 text-sm" "sx-source") " type is SX source text wrapped in an " (code :class "text-violet-700 text-sm" "SxExpr") " marker. It allows the host to pass pre-rendered SX markup into the tree — but only the host can create it. SX code cannot construct SxExpr values; it can only receive them from the boundary."))
(~doc-section :title "Enforcement" :id "enforcement"
(p :class "text-stone-600"
"The boundary contract is enforced at three points, each corresponding to a tier:")
(div :class "space-y-3"
(div :class "bg-stone-100 rounded p-4"
(p :class "text-sm text-stone-700"
(strong "Primitive registration. ") "When " (code :class "text-violet-700 text-sm" "@register_primitive") " decorates a function, it calls " (code :class "text-violet-700 text-sm" "validate_primitive(name)") ". If the name is not declared in " (code :class "text-violet-700 text-sm" "primitives.sx") ", the service fails to start."))
(div :class "bg-stone-100 rounded p-4"
(p :class "text-sm text-stone-700"
(strong "I/O handler registration. ") "When " (code :class "text-violet-700 text-sm" "primitives_io.py") " builds the " (code :class "text-violet-700 text-sm" "_IO_HANDLERS") " dict, each name is validated against " (code :class "text-violet-700 text-sm" "boundary.sx") ". Undeclared I/O primitives crash the import."))
(div :class "bg-stone-100 rounded p-4"
(p :class "text-sm text-stone-700"
(strong "Page helper registration. ") "When a service calls " (code :class "text-violet-700 text-sm" "register_page_helpers(service, helpers)") ", each helper name is validated against " (code :class "text-violet-700 text-sm" "boundary.sx") " for that service. Undeclared helpers fail. Return values are wrapped to pass through " (code :class "text-violet-700 text-sm" "validate_boundary_value()") ".")))
(p :class "text-stone-600"
"All three checks are controlled by the " (code :class "text-violet-700 text-sm" "SX_BOUNDARY_STRICT") " environment variable. With " (code :class "text-violet-700 text-sm" "\"1\"") " (the production default), violations crash at startup. Without it, they log warnings. The strict mode is set in both " (code :class "text-violet-700 text-sm" "docker-compose.yml") " and " (code :class "text-violet-700 text-sm" "docker-compose.dev.yml") "."))
(~doc-section :title "The SX-in-Python rule" :id "sx-in-python"
(p :class "text-stone-600"
"One enforcement that is not automated but equally important: " (strong "SX source code must not be constructed as Python strings") ". S-expressions belong in " (code :class "text-violet-700 text-sm" ".sx") " files. Python belongs in " (code :class "text-violet-700 text-sm" ".py") " files. If you see a Python f-string that builds " (code :class "text-violet-700 text-sm" "(div :class ...)") ", that is a boundary violation.")
(p :class "text-stone-600"
"The correct pattern: Python returns " (strong "data") " (dicts, lists, strings). " (code :class "text-violet-700 text-sm" ".sx") " files receive data via keyword args and compose the markup. The only exception is " (code :class "text-violet-700 text-sm" "SxExpr") " wrappers for pre-rendered fragments — and those should be built with " (code :class "text-violet-700 text-sm" "sx_call()") " or " (code :class "text-violet-700 text-sm" "_sx_fragment()") ", never with f-strings.")
(~doc-code :code (highlight ";; CORRECT: .sx file composes markup from data\n(defcomp ~my-page (&key items)\n (div :class \"space-y-4\"\n (map (fn (item)\n (div :class \"border rounded p-3\"\n (h3 (get item \"title\"))\n (p (get item \"desc\"))))\n items)))" "lisp"))
(~doc-code :code (highlight "# CORRECT: Python returns data\ndef _my_page_data():\n return {\"items\": [{\"title\": \"A\", \"desc\": \"B\"}]}\n\n# WRONG: Python builds SX source\ndef _my_page_data():\n return SxExpr(f'(div (h3 \"{title}\"))') # NO" "python")))
(~doc-section :title "Why this matters for multiple languages" :id "languages"
(p :class "text-stone-600"
"The boundary contract is target-agnostic. " (code :class "text-violet-700 text-sm" "boundary.sx") " and " (code :class "text-violet-700 text-sm" "primitives.sx") " declare what crosses the boundary. Each bootstrapper reads those declarations and emits the strongest enforcement the target language supports:")
(div :class "overflow-x-auto rounded border border-stone-200 my-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Target")
(th :class "px-3 py-2 font-medium text-stone-600" "Enforcement")
(th :class "px-3 py-2 font-medium text-stone-600" "Mechanism")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Python")
(td :class "px-3 py-2 text-stone-600" "Runtime")
(td :class "px-3 py-2 text-stone-500 text-sm" "Frozen sets + validation at registration"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "JavaScript")
(td :class "px-3 py-2 text-stone-600" "Runtime")
(td :class "px-3 py-2 text-stone-500 text-sm" "Set guards on registerPrimitive()"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Rust")
(td :class "px-3 py-2 text-stone-600" "Compile-time")
(td :class "px-3 py-2 text-stone-500 text-sm" "SxValue enum, trait bounds on primitive fns"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Haskell")
(td :class "px-3 py-2 text-stone-600" "Compile-time")
(td :class "px-3 py-2 text-stone-500 text-sm" "ADT + typeclass constraints"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "TypeScript")
(td :class "px-3 py-2 text-stone-600" "Compile-time")
(td :class "px-3 py-2 text-stone-500 text-sm" "Discriminated union types")))))
(p :class "text-stone-600"
"In Python and JavaScript, boundary violations are caught at startup. In Rust, Haskell, or TypeScript, they would be caught at compile time — a function that returns a non-boundary type simply would not type-check. The spec is the same; the enforcement level rises with the type system.")
(p :class "text-stone-600"
"This is the payoff of the pure island constraint. Because SX never touches host internals, a bootstrapper for a new target only needs to implement the declared primitives and boundary functions. The evaluator, renderer, parser, and all components work unchanged. One spec, every target, same guarantees."))
(~doc-section :title "The spec as contract" :id "contract"
(p :class "text-stone-600"
"The boundary enforcement files form a closed contract:")
(ul :class "space-y-2 text-stone-600"
(li (code :class "text-violet-700 text-sm" "primitives.sx") " — declares every pure function SX can call")
(li (code :class "text-violet-700 text-sm" "boundary.sx") " — declares every I/O function and page helper")
(li (code :class "text-violet-700 text-sm" "boundary_parser.py") " — reads both files, extracts declared names")
(li (code :class "text-violet-700 text-sm" "boundary.py") " — runtime validation (validate_primitive, validate_io, validate_helper, validate_boundary_value)"))
(p :class "text-stone-600"
"If you add a new host function and forget to declare it, the service will not start. If you return a disallowed type, the validation will catch it. The spec files are not documentation — they are the mechanism. The bootstrappers read them. The validators parse them. The contract is enforced by the same files that describe it.")
(p :class "text-stone-600"
"This closes the loop on self-hosting. The SX spec defines the language. The boundary spec defines the edge. The bootstrappers generate implementations from both. And the generated code validates itself against the spec at startup. The spec is the implementation is the contract is the spec."))))