Prompts Are S-Expressions
Following up on harness engineering: if the ideal agent platform is an intelligence layer mutating a stateful block of data, and part of that mutable state is the procedural layer of the agent’s own future executions (“code as prompts”), then we’ve rediscovered something old. This is Lisp’s “code is data is code” — homoiconicity — rebuilt on a new substrate.
The isomorphism
Lisp programs are expressed in the language’s own primary data structure, so the full power of the language is available for manipulating programs. The agent workspace has the same property by construction: the procedural layer — system prompts, skills, memory files — is plain text in the workspace, exactly the medium the intelligence natively reads and writes. The prompt layer isn’t a sealed artifact the way compiled code is; it sits inside the data domain the agent mutates.
Prompts are files are prompts.
| Lisp | Agent harness |
|---|---|
| s-expression | prompt/skill/memory file |
eval |
invoking an agent on constructed context |
apply |
tool call |
| the REPL loop | the agent loop |
| macro | skill/memory edit that reshapes future context assembly |
| quasiquote + unquote | prompt templating: literal instructions, state spliced in |
| macroexpansion time vs runtime | context-assembly time vs inference time |
| the image (Smalltalk/Lisp machine) | the persistent workspace + memory |
| metacircular evaluator | agent spawning agents with prompts it wrote |
| MOP / procedural reflection | programmatic edits over the prompt layer itself |
Two of these carry the most weight:
Skills are macros. A macro runs at compile time and returns code that
gets evaluated later. A skill “runs” at context-assembly time — text whose
effect is to transform the effective program of a future invocation. When
the agent writes a skill for itself, that’s defmacro: current-stage
execution generating next-stage procedural layer. Staged computation, and
Lisp has been thinking about its discipline (hygiene, capture) for forty
years.
Recursive spawn is eval with quasiquotation. An agent composing a
prompt for a subagent — template instructions, splice in current state,
dispatch — is `(do-task ,(current-findings)) followed by eval.
Programmatic invocation and prompt-layer programmability turn out to be the
same feature at different reflective levels: eval applied outward (spawn a
new evaluator) vs eval applied inward (rewrite your own definition). The
formalization of the inward case is Brian Cantwell Smith’s 3-Lisp and the
reflective tower.
Where the analogy breaks
The breaks are more illuminating than the mapping.
The evaluator is stochastic. Macroexpansion is deterministic; the semantics of an s-expression are exact. The agent’s evaluator is an LLM — interpretation is probabilistic. So this is a homoiconic system with a noisy eval, and each level of the reflective tower compounds interpretation error. Meta-programming in prompt-space has an error budget Lisp never had. Design consequence: keep the tower shallow, verify at each level (which may be why harnesses converge on judge/verifier patterns).
There is no quote operator. Lisp rigorously distinguishes mentioning
code from running it — '(rm -rf) is inert. In prompt-space, everything in
context is potentially live; no syntactic marker reliably makes text inert
to the model. Prompt injection is precisely an unquoting bug: data from
the domain accidentally evaluated as code. The whole injection problem,
restated: we built a homoiconic system without a quotation discipline. And
it’s clear why it’s hard — Lisp’s quoting is enforced by the evaluator,
but here the evaluator is the thing being fooled.
The compiler changes underneath you. An s-expression means the same thing on any conforming Lisp. A prompt’s semantics depend on the model version — code interpreted by an evolving evaluator you don’t control. (Lisp had this fight too: fexprs were abandoned partly because they made code impossible to statically analyze. Self-modifying prompt layers have the same opacity, squared.)
Natural language is a compression, not a formalism. This cuts both ways. You lose exactness, but the domain data and the code share not just a data structure but a semantics. In Lisp, code-as-data still requires the data to be code-shaped. In an agent, a journal entry can become an instruction with zero transformation. The homoiconicity is more total than Lisp’s — and that is the source of both the meta-programming power and the injection vulnerability. They are the same property.
The inherited research agenda
Take the mapping seriously and harness engineering inherits Lisp’s open problems:
- Hygiene for prompt-macros — when a skill rewrites future context, what prevents capture and interference with other skills? (Macro hygiene, 1986.)
- A quotation discipline — some harness-enforced way to mark context as data-not-instructions. The harness must enforce it, since the evaluator can’t.
- Reflective levels with narrow interfaces — 3-Lisp’s insight: a tower is manageable if each level exposes a small, explicit reflective API rather than raw self-access.
- The image problem — Lisp and Smalltalk images famously drifted into unreproducible snowflakes. A long-lived workspace accumulating self-written memory and skills has the same failure mode; git-tracked, declarative configuration is the countermeasure.
The thesis in one line: an agent workspace is a homoiconic system with a stochastic evaluator and no quote operator — it inherits Lisp’s meta-programming power and pays for it with Lisp’s abandoned problems (fexpr opacity, image drift) plus a new one: the evaluator itself can be persuaded.