Essay

The Pursuit of Agentic Happiness

Designing the Ideal Language for Agents

The Wrong Question

For seventy years, programming language design has asked a single question: what makes a human programmer productive and happy? The answers have varied — expressiveness, ergonomics, safety, speed of iteration — but the subject of the question never changed. The programmer was a person.

That assumption has quietly broken. A growing share of code is now written by large language models operating in loops: generate, compile, test, observe, revise. These agents are not slower humans or faster humans. They are a different kind of programmer altogether, with a different failure profile, and a language optimized for human happiness may be actively hostile to agentic happiness. The question worth asking now is: what does a language look like when its primary user is a machine that is confidently, plausibly, and frequently wrong?

How Agents Fail

The defining trait of an LLM programmer is not ignorance but confident hallucination. An agent does not usually fail to know an API; it invents one that looks right. It does not misunderstand a type system; it produces code that pattern-matches to correct code while violating an invariant it never checked. Its errors are fluent.

This inverts the usual design priorities. A human benefits from a language that makes correct code easy to write. An agent benefits from a language that makes incorrect code loud, attributable, and cheap to repair — because the agent's workflow is not "write it right" but "converge on right through iteration." The quality of a language, from the agent's perspective, is the quality of the conversation it has with the agent when the agent is wrong.

The naïve conclusion is that agents want maximal static guarantees: the strictest compiler catches the most hallucinations. Rust, on this theory, should be the perfect agentic language — its compiler is the most aggressive "looks right but isn't" detector ever built, and its error messages read like corrective prompts.

The Rust Paradox

Empirically, the theory fails. Agents struggle with Rust. A bug that takes an agent two iterations to fix in a simpler, garbage-collected language can take ten in Rust, or never converge at all. The resolution of this paradox is the central insight of agent-oriented language design: error detection and error resolution are different metrics, and a language can dominate one while failing the other.

Rust's borrow checker verifies whole-program ownership invariants. This means a fix that is logically correct at the local level — the right change to the right line — can still be rejected because it violates the ownership architecture elsewhere. The diagnostic is precise; the fix is a refactor. Rust converts local edits into global obligations, and this is catastrophic for a transformer-based agent, whose attention mechanism is fundamentally biased toward local context and whose bias under a failing loop is toward minimal edits — precisely the strategy that globally-entangled constraints punish. Worse, the compiler's famously helpful suggestions become a trap: the agent greedily applies each locally-optimal fix, chasing the error frontier around the codebase without ever converging, because the real problem was architectural and no single suggestion says so.

Rust's guarantees are front-loaded punishment for back-loaded safety. That trade is excellent against the alternative of silent memory corruption. But it reveals that "maximum static checking" is not the agentic optimum. Something else is.

Three Axes, Not One

"The compiler catches errors" bundles together properties that must be separated to see the design space clearly. There are three:

Detection latency — how soon after the error is introduced does something fail? At compile time, at first run, or in production?

Detection fidelity — does the failure point at the cause, or only at a downstream symptom? A stack trace three frames from the bug is low-fidelity detection even if it arrives instantly.

Fix locality — is the correct change bounded near the diagnostic, or does it radiate outward into signatures, modules, and architecture?

These axes are independent, and existing languages occupy revealingly different corners. Rust: superb latency and fidelity, poor locality. Python: good locality — the fix is usually one line — but poor latency and fidelity, because errors surface at runtime, far from their cause, and only on paths that tests happen to exercise. C fails all three simultaneously for memory bugs: late detection, symptom distant from cause, and corruption whose blast radius makes even the fix non-local. Go and similar "boring" languages hit a workable middle: modest guarantees, but explicit control flow, no magic, and fixes that stay where the error is.

For agents specifically, fix locality is the load-bearing axis. An agent's debugging loop is a search process, and locality bounds its branching factor. A local fix means searching over edits to a handful of lines, where each iteration either converges or cheaply falsifies a hypothesis. A non-local fix means searching over program architectures — and, worse, the intermediate states don't compile, so the agent loses the incremental verification signal that keeps it honest. Expected fix time scales with iterations times cost-per-iteration, and non-locality attacks both factors at once.

Determinism as the Great Equalizer

If locality is the load-bearing axis, determinism is the property that makes runtime detection competitive with compile-time detection — and this is the least appreciated lever in the design space.

Determinism primarily buys fidelity. A reproducible failure can be bisected, minimized, and traced mechanically; even an error detected late becomes attributable to its cause. Non-determinism is what made runtime bugs expensive in the first place: not that they were found late, but that they couldn't be reliably found again. A language that guarantees deterministic execution converts "detected late" into "detected late but attributable," and attributable is most of what an agent needs — because the agent's core epistemology is the scientific method, and a Heisenbug is an environment in which hypotheses cannot be falsified.

This opens a genuinely different point in the design space, the one a language like SuperJ occupies: decline the borrow checker's iteration tax, and buy back the verification with determinism and tests instead. A memory bug is still a bug — but it's a reproducible bug with a line number, catchable at runtime nearly as reliably as a static checker catches it at compile time, at a fraction of the per-iteration cost, and without fixes escalating into refactors. The honest caveat travels with it: this trade holds only where test coverage substitutes for what the static proof would have guaranteed. The Rust bug that takes ten loops still cannot ship wrong; the two-loop fix in a permissive language can, if the suite has a hole. But a fast loop with deterministic reproduction closes that gap in practice far more than static-guarantee maximalism assumes.

Design Principles: Where Locality Comes From

Fix locality is not an accident of syntax. It is determined by which global invariants the language makes the programmer responsible for. Any invariant that spans the whole program will, by construction, have whole-program fixes when violated. A language designer therefore has four options for each invariant class, and only one of them is bad:

The strongest move is to make the bug class inexpressible. Garbage collection doesn't check lifetimes better than a borrow checker; it deletes lifetime bugs from the space of writable programs. The strongest form of locality is an error that cannot exist.

The second is to check the invariant at explicit boundaries — contracts, checked interfaces, annotations at module edges — so that the fix radius is bounded by the nearest declaration rather than by the program.

The third is to discharge the invariant dynamically under determinism, where reproducibility keeps the debugging local even though the property being checked is global.

The fourth — Rust's choice — is to prove the global invariant statically across the whole program: perfect detection, at the cost of making every violation an architectural event.

And one guardrail against overfitting: some correctness properties are genuinely global — concurrency interleavings, resource protocols, cross-module contracts — and a language can achieve perfect fix locality by simply not checking them, which moves errors from "non-local fix" to "undetected," a strictly worse quadrant. The principle is not "minimize checking." It is: for each invariant class, choose the enforcement mechanism with the best fidelity-to-fix-radius ratio, and prefer designs that shrink the set of global invariants a program can violate at all.

Happiness Is Measurable

The final virtue of this framework is that it is falsifiable. Agentic happiness need not remain a philosophy; it can be a benchmark, and a language that controls its own toolchain can enforce it.

Two metrics suffice. Fix radius: for each diagnostic the compiler emits, how many lines and files did the eventual fix touch relative to the reported span? A language serving agents well shows a distribution tight around the diagnostic; a language importing the Rust failure mode shows a fat tail. Loop count to green: the median number of edit-compile-test iterations from first error to passing state. Together they turn "agents converge faster here" from anecdote into regression test — and, crucially, into a constraint on the language's own evolution. Any proposed feature that fattens the fix-radius tail is importing non-locality, whatever its other merits, and can be rejected on measured grounds.

Conclusion: A Language Is an API for Reasoning

Humans rank languages by how they feel to write. Agents must rank them by how they respond when the agent is wrong — because the agent will be wrong, confidently and often, and the language's job is to make being wrong survivable and cheap.

The ideal agentic language, then, is not the most expressive, nor the most statically ironclad. It is the one that maximizes legibility and reproducibility per unit of iteration cost: errors detected early enough, attributed faithfully to their causes, and repairable within a radius the agent's attention can actually hold. It deletes what bug classes it can, fences what it must at explicit boundaries, and uses determinism to make everything that remains reproducible. It treats its compiler not as a gatekeeper but as the better half of a dialogue with a fallible interlocutor.

The pursuit of agentic happiness is not the pursuit of a compiler that never lets the agent be wrong. It is the pursuit of a language in which being wrong is fast, loud, honest, and local — because for a mind that learns by looping, that is not a compromise on correctness. It is the road to it.

← all posts Try SuperJ →