Files
rose-ash/sx/sx/essays/sx-and-ai.sx
giles 6f96452f70 Fix empty code blocks: rename ~docs/code param, fix batched IO dispatch
Two bugs caused code blocks to render empty across the site:

1. ~docs/code component had parameter named `code` which collided with
   the HTML <code> tag name. Renamed to `src` and updated all 57
   callers. Added font-mono class for explicit monospace.

2. Batched IO dispatch in ocaml_bridge.py only skipped one leading
   number (batch ID) but the format has two (epoch + ID):
   (io-request EPOCH ID "name" args...). Changed to skip all leading
   numbers so the string name is correctly found. This fixes highlight
   and other batchable helpers returning empty results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:08:40 +00:00

110 lines
18 KiB
Plaintext

(defcomp ~essays/sx-and-ai/essay-sx-and-ai ()
(~docs/page :title "SX and AI"
(p :class "text-stone-500 text-sm italic mb-8"
"Why s-expressions are the most AI-friendly representation for web interfaces — and what that means for how software gets built.")
(~docs/section :title "The syntax tax" :id "syntax-tax"
(p :class "text-stone-600"
"Every programming language imposes a syntax tax on AI code generation. The model must produce output that satisfies a grammar — matching braces, semicolons in the right places, operator precedence, indentation rules, closing tags that match opening tags. The more complex the grammar, the more tokens the model wastes on syntactic bookkeeping instead of semantic intent.")
(p :class "text-stone-600"
"Consider what an AI must get right to produce a valid React component: JSX tags that open and close correctly, curly braces for JavaScript expressions inside markup, import statements with correct paths, semicolons or ASI rules, TypeScript type annotations, CSS-in-JS string literals with different quoting rules than the surrounding code. Each syntactic concern is a potential failure point. Each failure produces something that does not parse, let alone run.")
(p :class "text-stone-600"
"S-expressions have one syntactic form: " (code "(head args...)") ". Parentheses open and close. Strings are quoted. That is the entire grammar. There is no operator precedence because there are no operators. There is no indentation sensitivity because whitespace is not significant. There are no closing tags because there are no tags — just lists.")
(p :class "text-stone-600"
"The syntax tax for SX is essentially zero. An AI that can count parentheses can produce syntactically valid SX. This is not a small advantage — it is a categorical one. The model spends its capacity on " (em "what") " to generate, not " (em "how") " to format it."))
(~docs/section :title "One representation for everything" :id "one-representation"
(p :class "text-stone-600"
"A typical web project requires the AI to context-switch between HTML (angle brackets, void elements, boolean attributes), CSS (selectors, properties, at-rules, a completely different syntax from HTML), JavaScript (statements, expressions, classes, closures, async/await), and whatever templating language glues them together (Jinja delimiters, ERB tags, JSX interpolation). Each is a separate grammar. Each has edge cases. Each interacts with the others in ways that are hard to predict.")
(p :class "text-stone-600"
"In SX, structure, style, logic, and data are all s-expressions:")
(~docs/code :src (highlight ";; Structure\n(div :class \"card\" (h2 title) (p body))\n\n;; Style\n(cssx card-style\n :bg white :rounded-lg :shadow-md :p 6)\n\n;; Logic\n(if (> (length items) 0)\n (map render-item items)\n (p \"No items found.\"))\n\n;; Data\n{:name \"Alice\" :role \"admin\" :active true}\n\n;; Component definition\n(defcomp ~essays/sx-and-ai/user-card (&key user)\n (div :class \"card\"\n (h2 (get user \"name\"))\n (span :class \"badge\" (get user \"role\"))))" "lisp"))
(p :class "text-stone-600"
"The AI learns one syntax and applies it everywhere. The mental model does not fragment across subsystems. A " (code "div") " and an " (code "if") " and a " (code "defcomp") " are all lists. The model that generates one can generate all three, because they are the same thing."))
(~docs/section :title "The spec fits in a context window" :id "spec-fits"
(p :class "text-stone-600"
"The complete SX language specification — evaluator, parser, renderer, primitives — lives in four files totalling roughly 3,000 lines. An AI model with a 200k token context window can hold the " (em "entire language definition") " alongside the user's codebase and still have room to work. Compare this to JavaScript (the " (a :href "https://ecma-international.org/publications-and-standards/standards/ecma-262/" :class "text-violet-600 hover:underline" "ECMAScript specification") " is 900+ pages), or the combined specifications for HTML, CSS, and the DOM.")
(p :class "text-stone-600"
"This is not just a convenience — it changes what kind of code the AI produces. When the model has the full spec in context, it does not hallucinate nonexistent features. It does not confuse one version's semantics with another's. It knows exactly which primitives exist, which special forms are available, and how evaluation works — because it is reading the authoritative definition, not interpolating from training data.")
(p :class "text-stone-600"
"The spec is also written in SX. " (code "eval.sx") " defines the evaluator as s-expressions. " (code "parser.sx") " defines the parser as s-expressions. The language the AI is generating is the same language the spec is written in. There is no translation gap between \"understanding the language\" and \"using the language\" — they are the same act of reading s-expressions."))
(~docs/section :title "Structural validation is trivial" :id "structural-validation"
(p :class "text-stone-600"
"Validating AI output before executing it is a critical safety concern. With conventional languages, validation means running a full parser, type checker, and linter — each with their own error recovery modes and edge cases. With SX, structural validation is: " (em "do the parentheses balance?") " That is it. If they balance, the expression parses. If it parses, it can be evaluated.")
(p :class "text-stone-600"
"This makes it trivial to build AI pipelines that generate SX. Parse the output. If it parses, evaluate it in a sandbox. If it does not parse, the error is always the same kind — unmatched parentheses — and the fix is always mechanical. There is no \"your JSX is invalid because you used " (code "class") " instead of " (code "className") "\" or \"you forgot the semicolon after the type annotation but before the generic constraint.\"")
(p :class "text-stone-600"
"Beyond parsing, the SX " (a :href "/sx/(language.(spec.primitives))" :class "text-violet-600 hover:underline" "boundary system") " provides semantic validation. A pure component cannot call IO primitives — not by convention, but by the evaluator refusing to resolve them. An AI generating a component can produce whatever expressions it wants; the sandbox ensures only permitted operations execute. Validation is not a separate step bolted onto the pipeline. It is the language."))
(~docs/section :title "Components are self-documenting" :id "self-documenting"
(p :class "text-stone-600"
"A React component's interface is spread across prop types (or TypeScript interfaces), JSDoc comments, Storybook stories, and whatever documentation someone wrote. An AI reading a component must synthesize information from multiple sources to understand what it accepts and what it produces.")
(p :class "text-stone-600"
"An SX component declares everything in one expression:")
(~docs/code :src (highlight "(defcomp ~essays/sx-and-ai/product-card (&key title price image &rest children)\n (div :class \"rounded border p-4\"\n (img :src image :alt title)\n (h3 :class \"font-bold\" title)\n (span :class \"text-lg\" (format-price price))\n children))" "lisp"))
(p :class "text-stone-600"
"The AI reads this and knows: it takes " (code "title") ", " (code "price") ", and " (code "image") " as keyword arguments, and " (code "children") " as rest arguments. It knows the output structure — a " (code "div") " with an image, heading, price, and slot for children. It knows this because the definition " (em "is") " the documentation. There is no separate spec to consult, no type file to find, no ambiguity about which props are required.")
(p :class "text-stone-600"
"This self-describing property scales across the entire component environment. An AI can " (code "(map ...)") " over every component in the registry, extract all parameter signatures, build a complete map of the UI vocabulary — and generate compositions that use it correctly, because the interface is declared in the same language the AI is generating."))
(~docs/section :title "Token efficiency" :id "token-efficiency"
(p :class "text-stone-600"
"LLMs operate on tokens. Every token costs compute, latency, and money. The information density of a representation — how much semantics per token — directly affects how much an AI can see, generate, and reason about within its context window and output budget.")
(p :class "text-stone-600"
"Compare equivalent UI definitions:")
(~docs/code :src (highlight ";; SX: 42 tokens\n(div :class \"card p-4\"\n (h2 :class \"font-bold\" title)\n (p body)\n (when footer\n (div :class \"mt-4 border-t pt-2\" footer)))" "lisp"))
(~docs/code :src (highlight "// React/JSX: ~75 tokens\n<div className=\"card p-4\">\n <h2 className=\"font-bold\">{title}</h2>\n <p>{body}</p>\n {footer && (\n <div className=\"mt-4 border-t pt-2\">{footer}</div>\n )}\n</div>" "python"))
(p :class "text-stone-600"
"The SX version is roughly 40% fewer tokens for equivalent semantics. No closing tags. No curly-brace interpolation. No " (code "className") " vs " (code "class") " distinction. Every token carries meaning. Over an entire application — dozens of components, hundreds of expressions — this compounds into significantly more code visible per context window and significantly less output the model must generate."))
(~docs/section :title "Composability is free" :id "composability"
(p :class "text-stone-600"
"The hardest thing for AI to get right in conventional frameworks is composition — how pieces fit together. React has rules about hooks. Vue has template vs script vs style sections. Angular has modules, declarations, and dependency injection. Each framework's composition model is a set of conventions the AI must learn and apply correctly.")
(p :class "text-stone-600"
"S-expressions compose by nesting. A list inside a list is a composition. There are no rules beyond this:")
(~docs/code :src (highlight ";; Compose components by nesting — that's it\n(~page-layout :title \"Dashboard\"\n (~sidebar\n (~nav-menu :items menu-items))\n (~main-content\n (map ~essays/sx-and-ai/user-card users)\n (~pagination :page current-page :total total-pages)))" "lisp"))
(p :class "text-stone-600"
"No imports to manage. No registration steps. No render props, higher-order components, or composition APIs. The AI generates a nested structure and it works, because nesting is the only composition mechanism. This eliminates an entire class of errors that plague AI-generated code in conventional frameworks — the kind where each piece works in isolation but the assembly is wrong."))
(~docs/section :title "The feedback loop" :id "feedback-loop"
(p :class "text-stone-600"
"SX has no build step. Generated s-expressions can be evaluated immediately — in the browser, on the server, in a test harness. The AI generates an expression, the system evaluates it, the result is visible. If it is wrong, the AI reads the result (also an s-expression), adjusts, and regenerates. The loop is:")
(ol :class "list-decimal pl-5 text-stone-600 space-y-1 mt-2"
(li "AI generates SX expression")
(li "System parses (parentheses balance? done)")
(li "Evaluator runs in sandbox (boundary-enforced)")
(li "Result rendered or error returned (as s-expression)")
(li "AI reads result, iterates"))
(p :class "text-stone-600"
"Compare this to the conventional loop: AI generates code → linter runs → TypeScript compiles → bundler builds → browser loads → error appears in DevTools console → human copies error back to AI → AI regenerates. Each step is a different tool with different output formats. Each introduces latency and potential information loss.")
(p :class "text-stone-600"
"The SX loop is also " (em "uniform") ". The input is s-expressions. The output is s-expressions. The error messages are s-expressions. The AI never needs to parse a stack trace format or extract meaning from a webpack error. Everything is the same data structure, all the way down."))
(~docs/section :title "This site is the proof" :id "proof"
(p :class "text-stone-600"
"This is not theoretical. Everything you are looking at — every page, every component, every line of this essay — was produced by agentic AI. Not \"AI-assisted\" in the polite sense of autocomplete suggestions. " (em "Produced.") " The SX language specification. The parser. The evaluator. The renderer. The bootstrappers that transpile the spec to JavaScript and Python. The boundary enforcement system. The dependency analyser. The on-demand CSS engine. The client-side router. The component bundler. The syntax highlighter. This documentation site. The Docker deployment. All of it.")
(p :class "text-stone-600"
"The human driving this has never written a line of Lisp. Not Common Lisp. Not Scheme. Not Clojure. Not Emacs Lisp. Has never opened the codebase in VS Code, vi, or any other editor. Every file was created and modified through " (a :href "https://claude.ai/" :class "text-violet-600 hover:underline" "Claude") " running in a terminal — reading files, writing files, running commands, iterating on errors. The development environment is a conversation.")
(p :class "text-stone-600"
"That this works at all is a testament to s-expressions. The AI generates " (code "(defcomp ~essays/sx-and-ai/card (&key title) (div :class \"p-4\" (h2 title)))") " and it is correct on the first attempt, because there is almost nothing to get wrong. The AI generates a 300-line spec file defining evaluator semantics and every parenthesis balances, because balancing parentheses is the " (em "only") " syntactic constraint. The AI writes a bootstrapper that reads " (code "eval.sx") " and emits JavaScript, and the output runs in the browser, because the source and target are both trees.")
(p :class "text-stone-600"
"Try this with React. Try generating a complete component framework — parser, evaluator, renderer, type system, macro expander, CSS engine, client router — through pure conversation with an AI, never touching an editor. The syntax tax alone would be fatal. JSX irregularities, hook ordering rules, import resolution, TypeScript generics, webpack configuration, CSS module scoping — each is a class of errors that burns tokens and breaks the flow. S-expressions eliminate all of them."))
(~docs/section :title "The development loop" :id "dev-loop"
(p :class "text-stone-600"
"The workflow looks like this: describe what you want. The AI reads the existing code — because it can, because s-expressions are transparent to any reader. It generates new expressions. It writes them to disk. It runs the server. It checks the output. If something breaks, it reads the error, adjusts, and regenerates. The human steers with intent; the AI handles the syntax, the structure, and the mechanical correctness.")
(p :class "text-stone-600"
"This is only possible because the representation is uniform. The AI does not need to switch between \"writing HTML mode\" and \"writing CSS mode\" and \"writing JavaScript mode\" and \"writing deployment config mode.\" It is always writing s-expressions. The cognitive load is constant. The error rate is constant. The speed is constant — regardless of whether it is generating a page layout, a macro expander, or a Docker healthcheck.")
(p :class "text-stone-600"
"The " (a :href "/sx/(etc.(essay.sx-sucks))" :class "text-violet-600 hover:underline" "sx sucks") " essay copped to the AI authorship and framed it as a weakness — microwave dinner on a nice plate. But the framing was wrong. If a language is so well-suited to machine generation that one person with no Lisp experience can build a self-hosting language, a multi-target bootstrapper, a reactive component framework, and a full documentation site through pure agentic AI — that is not a weakness of the language. That is the language working exactly as it should."))
(~docs/section :title "What this changes" :id "what-changes"
(p :class "text-stone-600"
"The question is not whether AI will generate user interfaces. It already does. The question is what representation makes that generation most reliable, most efficient, and most safe. S-expressions — with their zero-syntax-tax grammar, uniform structure, self-describing components, structural validation, and sandboxed evaluation — are a strong answer.")
(p :class "text-stone-600"
"Not because they were designed for AI. " (a :href "https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)" :class "text-violet-600 hover:underline" "McCarthy") " invented them in 1958, decades before anyone imagined language models. But the properties that make s-expressions elegant for humans — minimalism, uniformity, composability, homoiconicity — turn out to be exactly the properties that make them tractable for machines. The simplest possible syntax is also the most machine-friendly syntax. This is not a coincidence. It is a consequence of what simplicity means.")
(p :class "text-stone-600"
"The era of AI-generated software is not coming. It is here. The question is which representations survive contact with it. The ones with the lowest syntax tax, the most uniform structure, and the tightest feedback loops will win — not because they are trendy, but because they are what the machines can actually produce reliably. S-expressions have been waiting sixty-seven years for a generation mechanism worthy of their simplicity. They finally have one."))))