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.