There are three things that turn the text a human writes into something a machine will actually do: a compiler, an interpreter, and a script. They are not fashions. They are the load-bearing wall of every program, including the ones agents now write for us. This post does two jobs. First, it states the difference plainly, because the difference keeps getting blurred in marketing copy. Second, it uses BoundaryML's BAML as an anti-case study: a tool that sells itself as the cure for agent slop while being, mechanically, more slop. The argument is not that the BAML team is bad at engineering. It is that they are solving the wrong problem, and the wrong problem is expensive.
The three translators
A computer does not read source code. It reads machine code, the 0s and 1s that map onto CPU instructions. Everything a human writes in a higher-level language has to be translated down. The translator is the part of the stack that decides how that translation happens, and the choice is not cosmetic: it changes how fast the program runs, when you find out it is broken, and how much memory the toolchain itself eats. GeeksforGeeks frames the whole field as language processors, of which the compiler and interpreter are the two high-level cases and the assembler is the low-level one. (Language Processors: Assembler, Compiler and Interpreter)
What a compiler actually does
A compiler is a translator that takes the whole high-level source program as input and emits low-level code, machine or assembly, as output. Because it sees the whole program before it runs anything, it can optimize across the entire codebase and report errors in a single batch at the end of compilation. The tradeoff is that any change to the source requires recompiling, and the compiler itself needs more memory to hold the intermediate representation and the generated object code. (Difference Between Compiler and Interpreter)
The payoff is speed and a security story. Compiled code runs faster than interpreted code because the translation is done once, ahead of time, and the runtime is not paying for analysis on every execution. GeeksforGeeks also notes that compilers are "more useful" for security than interpreters, because the ahead-of-time translation gives you a fixed artifact you can audit, sign, and ship, rather than a live translator running in your threat boundary. (Language Processors)
What an interpreter actually does
An interpreter translates a single statement into machine code and executes it immediately, then moves to the next line. If a statement errors, the interpreter stops right there and shows the error, which makes debugging easier because the failure is local. The cost is that no object code is saved, so the translation tax is paid every run, and the overall execution time is slower than a compiled equivalent. The interpreter also needs less memory than a compiler because it never materializes a full object artifact. (Language Processors)
The honest version of the comparison is a tradeoff table, not a winner:
| Property | Compiler | Interpreter |
|---|---|---|
| Reads | Whole program at once | One statement at a time |
| Object code | Generated and saved (e.g. .exe) | Not generated |
| Analysis time | Longer upfront | Shorter upfront |
| Execution time | Faster | Slower |
| Memory | More (holds IR + object) | Less (no object) |
| Errors | Reported in a batch after compile | Reported line-by-line, stops at first |
| Change cost | Recompile the whole thing | No retranslation needed |
| Examples | C, C++, C# | Python, Perl, JavaScript, Ruby |
Where the script fits
A scripting language is not a third kind of translator. It is a programming language that is traditionally run by an interpreter inside a host environment, without an explicit compile step. The key claim from GeeksforGeeks is direct: "all scripting languages are programming languages. The main theoretical difference is that scripting languages don't need a compilation step and are interpreted." (What's the difference between Scripting and Programming Languages?)
The important caveat, and the one most marketing copy omits, is that the boundary is about the execution environment, not the language. The same GeeksforGeeks article points out that C can be interpreted and used as a scripting language, and JavaScript can be compiled and used as a non-scripting language. The canonical example is Google Chrome's V8 engine, which compiles JavaScript to machine code instead of interpreting it. So "script" is a role a language plays in a given runtime, not a property of the syntax. (Scripting vs Programming)
The anti-case study: BAML
With the foundations stated, here is the case study. BAML is an open-source (Apache-2.0) project from Boundary, a company headquartered in Seattle, WA. Its own README title is the backronym "BAML: Basically A Made-up Language," and its one-line pitch is "BAML is the programming language for agents." (BoundaryML/baml README)
The README lists the features that are supposed to make agents produce less slop. Quoted directly from the repository:
BAML looks like TypeScript, but every feature is built so agents make fewer mistakes: * It has a type system like Rust, but compiles even faster than Go. * Types persist at runtime. There is no `any` nor casting dangerously to any type. * Errors are typed and statically analyzed. * The filesystem describes the modules/namespaces. * Has green threads, and colorless concurrency like Go * Built-in tests / eval framework * Built-in stdlib for agents * Every baml tool is natively designed for agents, with no garbage outputs, etc. * Can be run standalone or adopt incrementally (you can call a BAML function from TS, Py, Go, C#, Java, etc).
Read that list carefully, because it is the whole pitch, and every line is a feature that already exists in a foundational tool. The README even names the tools it is imitating: Rust, Go, TypeScript. That is the tell.
Every guardrail already has a home
The BAML claim is that agents need a new language to stop producing slop. The mechanical reality is that each claimed feature is a solved problem in a tool that has been hardened for years, often decades. Here is the mapping, claim by claim:
| BAML claim (from README) | Foundational tool that already does it |
|---|---|
| "type system like Rust" | Rust's borrow-checked type system, plus the ML family (OCaml, Haskell) and TypeScript in --strict mode. Type systems are a compiler feature, not a language. |
| "compiles even faster than Go" | Go already compiles fast; this is a runtime property of the toolchain, not a guardrail against agent mistakes. |
"Types persist at runtime, no any" | Runtime type reflection: Java, .NET, Python with typing + pydantic, TypeScript with type guards, zod, io-ts. "No any" is just strict mode. |
| "Errors are typed and statically analyzed" | RustResult/Option, Haskell Either, Swift, Kotlin sealed classes. Typed errors are standard in any modern typed language. |
| "Filesystem describes modules/namespaces" | Go (package = directory), Python (package = directory), Node (file = module), Java (package = directory). This is the conventional default, not an innovation. |
| "green threads, colorless concurrency like Go" | Go goroutines, Erlang processes, Java virtual threads (JEP 444, JDK 21), Kotlin coroutines, Rust async with tokio. Colorless concurrency is Go's existing design. |
| "Built-in tests / eval framework" | pytest, jest, go test, cargo test; for LLM evals specifically: promptfoo, langfuse, deepeval, guardrails-ai. |
| "Built-in stdlib for agents" | Every language ships a stdlib. Agent helpers are libraries (langchain, llamaindex), not a reason to invent a language. |
| "no garbage outputs" / "guardrails" / "structured-data" | JSON Schema, Pydantic, zod, serde, protobuf, and OpenAI's structured outputs / function calling. Schema validation is the actual guardrail; a language is not. |
The pattern is consistent. BAML is not introducing a capability that was missing. It is re-implementing capabilities that already exist, in a new syntax, and selling the syntax as the safety. That is the definition of slop wearing a guardrail costume.
The attack-surface argument
Here is where the compiler/interpreter/script distinction matters. BAML is not a library you call. It is a new language, which means a new stack of trust boundaries. To use BAML you install, per the README, brew install baml, then baml agent install, baml init, and baml ide install --code. Each of those is a binary running on developer machines and in CI. Underneath, the repository ships a parser, a type checker, a compiler, a runtime, a CLI (baml-cli), and FFI bridges to six host languages: TypeScript, Python, Go, C#, Java, and more. (BoundaryML/baml repository tree)
Every one of those components is a new attack surface:
.baml file now crosses it.brew install baml is a supply-chain trust event. The binary runs in CI and on laptops.any" guarantee can be defeated by an untyped crossing. The guardrail dies at the FFI.baml agent install — an installer that fetches and runs agent tooling is a remote-code-execution path dressed as a convenience.Compare that to the foundational tools BAML imitates. gcc, rustc, go, python, node, jq, and the POSIX shell are already packaged by every major distribution, have decades of CVE history and patching, and are audited by people whose full job is auditing them. Adding a new language does not subtract that surface. It adds a parallel surface on top of it, and then asks you to trust that the new one is sound because it is "like Rust."
They are solving the wrong problem
This is the core of it, and it is worth stating plainly so it does not sound like a cheap shot. The real problem with agent-generated code is not the absence of a typed language. Agents can already write Rust, TypeScript in strict mode, and Python with pydantic. The slop does not come from the language lacking types. It comes from the agent not running the type checker, not reading the compiler errors, not running the tests, and not validating structured output against a schema. Those are discipline and workflow failures, not language failures.
BAML's premise is that you fix agent slop by moving the agent into a language that forbids the mistakes. But the same outcome, without a new language, comes from a workflow rule that already exists in any serious codebase: the agent must run rustc / tsc --strict / pytest, must treat non-zero exit as a hard stop, and must validate every LLM output against a JSON Schema or pydantic model before it leaves the boundary. That is a constraint on the agent's behavior, enforced by the existing toolchain. BAML relocates the slop to a new surface instead of removing it: the agent now makes mistakes in BAML, against a younger type checker with a thinner CVE history, and crosses six FFIs to get back to the languages you actually ship.
The "wrong problem" framing is exact. BAML asks: how do we give agents a language that stops them producing slop? The right question is: how do we make agents use the verification tools we already have? The first question yields a new language, a new runtime, a new CLI, and six new trust boundaries. The second question yields a workflow change that costs nothing and reuses a toolchain that has been hardened since before the company existed.
Being fair to BAML
This is not a claim that the BAML team is incompetent. The repository shows real engineering: 3,755 commits, a Rust-based toolchain, an Apache-2.0 license, 9.1k stars and 486 forks at the time of writing, a typed-error design, and a genuine attempt at a fast compiler. (BoundaryML/baml) The team is openly hiring Rust engineers, which suggests they take the hard parts seriously. The critique here is about the premise, not the people or the craft. A well-built solution to the wrong problem is still the wrong problem, and "well-built" makes it more convincing, which is exactly why the premise deserves to be checked.
It is also worth saying what BAML is not being accused of here. It is not accused of being malware, of telemetry abuse, or of bad faith. The README's TELEMETRY.md is present and the license is permissive. The accusation is narrower and, I think, more useful: BAML is an anti-slop tool that is itself slop, because it re-implements existing foundational capability in a new syntax and sells the syntax as safety, while widening the attack surface it claims to narrow. That is a structural critique, and it applies to the category, not just this one project.
Caveats and fact-check notes
- Verified from the BAML README (fetched 2026-08-23): the backronym "Basically A Made-up Language," the pitch "the programming language for agents," the feature list quoted verbatim, the install commands, the FFI list (TS, Py, Go, C#, Java), Apache-2.0 license, Seattle HQ, and the topic tags
guardrails/llm/structured-data. Star/fork/commit counts (9.1k / 486 / 3,755) are point-in-time from the GitHub page and will drift. - Verified from GeeksforGeeks (all three URLs fetched and returned 200): the compiler/interpreter definitions, the whole-program vs line-by-line distinction, the object-code/no-object-code tradeoff, the "all scripting languages are programming languages" claim, and the V8-compiles-JavaScript example.
- Interpretation, not fact: the mapping of each BAML claim to an existing tool is an argument, not a quote. The existing tools named (Rust, Go, pydantic, zod, JSON Schema, pytest, guardrails-ai, JEP 444 virtual threads, etc.) are real and do what the table says, but the claim that BAML "re-implements" them is this post's thesis, not BAML's admission.
- Attack-surface claims are structural reasoning about what a new language + runtime + CLI + FFI bridges necessarily add; they are not a claim that BAML has specific CVEs. The point is that the surface is added before any CVE is found, which is the cost.
- "Wrong problem" framing is an opinion, stated as such. It is the post's conclusion, not an external verdict.
What to do instead
If the goal is less agent slop, the foundational tools already do the job and the workflow is the lever:
rustc / tsc --strict / mypy exit codes as hard stops. The agent does not merge until the existing checker is clean.pytest / go test / cargo test are the eval framework. For LLM-specific evals use promptfoo or guardrails-ai.The compiler, the interpreter, and the script have been doing this work for fifty years. The guardrails are not missing. They are sitting right there, in the toolchain the new language is asking you to replace.
