# Explore BAML
> BAML is the programming language for agents.
BAML is a Turing-complete programming language designed for a world where agents write and run a growing share of the code. It aims to prevent context pollution and churn when coding with AI: mistakes should be difficult to represent, behavior should be inspectable, and the language should remain dynamic enough for agents to create and execute programs.
In one sentence: BAML feels like TypeScript, but with better error handling, no `any`, and agent-first tooling.
Canonical web page: https://boundaryml.com/explore
## Contents
1. [Design philosophy](#design-philosophy)
2. [A better language](#a-better-language)
3. [Tools for agents](#tools-for-agents)
4. [Tools for humans](#tools-for-humans)
5. [Adopting BAML](#adopting-baml)
6. [Building agents](#building-agents)
7. [Try BAML](#try-baml)
## Design philosophy
### Invent as little as necessary
The more BAML invents, the worse agents will be at using it. Anything that differs from languages agents already know should be a deliberate decision with a clear payoff.
### Read like TypeScript, without the footguns
Agents and humans both work well with TypeScript's familiar types, unions, and generics. But TypeScript sits on top of JavaScript and necessarily retains escape hatches that agents tend to abuse:
```typescript
// The model reply is only a string, but this unchecked cast compiles.
const user = reply as unknown as User
```
BAML parses and validates values instead. There is no unchecked cast to write:
```baml
function ExtractUser(text: string) -> User
```
### Make undesired states unrepresentable
An agent sampling tokens will eventually produce an invalid state. If that state cannot compile, it cannot ship, and a human does not have to catch it during review.
```baml
class Ticket {
status: "open" | "closed",
}
```
`"opne"` is not a valid status, so it fails before runtime.
### Trace nondeterminism
Today is the most code humans will ever read. As more code is generated, the practical way to understand a system will increasingly be through focused traces and replay rather than reading every source file.
### Leave one obvious way
Every option eventually gets used by some agent. A codebase with five equivalent patterns invites the next agent to add a sixth. BAML prefers one predictable path.
### Keep edits local
When a change is not local, an agent goes on a side quest across the codebase. It returns with a polluted context full of unrelated files. BAML tries to make definitions discoverable by name and to keep changes close to the code they affect.
### Build tools for agents, not only IDEs
Humans still need editors, graphs, and debuggers. Agents also need first-class ways to discover and operate on code without hovering, clicking, or interpreting a visual interface.
## Install BAML
Just want to install it and run something? Install the toolchain and run a project:
```sh
# macOS or Linux (Homebrew)
brew install baml
# or: curl -fsSL https://pkg.boundaryml.com/install.sh | sh -s
baml init
baml agent install
baml run main
```
Full quickstart, editor setup, and more options: https://boundaryml.com/quickstart.md
## A better language
BAML starts with familiar syntax and a type system that resembles TypeScript, then changes the parts that become dangerous when agents write most of the code.
### Types exist at runtime
TypeScript explicitly chose not to be sound, trading correctness for human productivity. That was a reasonable choice for its ecosystem, but it is the wrong default when an agent can confidently add an unchecked cast.
BAML has no `any`. Types mean at runtime what the program says they mean. The language includes unions, generics, recursive types, and interfaces.
In TypeScript, this compiles and fails later:
```typescript
interface User {
name: string
email: string
}
const user = JSON.parse(raw) as User
user.email.toLowerCase()
```
In BAML, an `unknown` value must be proven to have the expected type:
```baml
class User {
name: string,
email: string,
}
function load(raw: unknown) -> string {
if (raw is User) {
raw.email.to_lower_case()
} else {
// This does not compile. `raw` is still unknown.
raw.email.to_lower_case()
}
}
```
### Match on types or values
BAML pattern matching replaces grab-bags of `typeof`, `instanceof`, and property-existence checks. The compiler can verify that the match is exhaustive.
```baml
function route(msg: Refund | Question | string) -> string {
match (msg) {
Refund => `refund ${msg.id}`,
Question { text } => `answer: ${text}`,
string => `text: ${msg}`,
}
}
function grade(n: int) -> string {
match (n) {
100 => "perfect",
let score if score >= 60 => "pass",
_ => "fail",
}
}
class Refund { id: string }
class Question { text: string }
```
Design proposal: [BEP-015](https://beps.boundaryml.com/beps/15).
### Typed error handling
TypeScript exceptions are untyped. In BAML, every `throws` declaration contributes to the inferred error set. Catch arms are matched by type, and the compiler can identify missing or impossible arms.
```baml
function show(ok: bool) -> string {
fetch_page(ok) catch (error) {
NetError => "recovered: " + error.detail,
// Compiler warning: this arm is unreachable because fetch_page
// cannot throw ParseError.
ParseError => "unreachable",
}
}
function fetch_page(ok: bool) -> string {
if (!ok) {
throw NetError { detail: "timeout" }
}
""
}
class NetError { detail: string }
class ParseError { detail: string }
```
Design proposal: [BEP-002](https://beps.boundaryml.com/beps/02).
### Green threads: async without function coloring
BAML follows Go's approach to concurrency with a TypeScript-like feel. `spawn` can start any function concurrently; the function and its entire call graph do not need separate `async` declarations.
```baml
function main() -> int {
let a = spawn { work(1) };
let b = spawn { work(2) };
let c = spawn { work(3) };
(await a) + (await b) + (await c)
}
function work(i: int) -> int {
i * i
}
test "spawn and await" {
assert.equal(main(), 14)
}
```
This works for slow LLM requests and tool calls, but it is not limited to I/O. CPU-bound BAML work can run across cores too.
In a benchmark that scans 38.4 GB of text:
| Runtime | Time | CPU usage |
| --- | ---: | ---: |
| Bun, one thread | 8.2 s | 1 core |
| BAML, one thread | 6.8 s | 1 core |
| BAML, `spawn` ×16 | 0.87 s | about 10 cores |
The benchmark uses 16 shards, each scanning roughly 48 MB of text 50 times. BAML's standard-library string search is implemented in native Rust; Bun still wins per core for tight arithmetic loops where its JIT has an advantage.
Design proposal: [BEP-034](https://beps.boundaryml.com/beps/34).
## Tools for agents
BAML includes tools that help agents find, run, test, and distribute code without reconstructing the project through repeated grep and file reads.
### The filesystem is the namespace structure
AI agents spend too much time searching large projects. In BAML, namespace directories use an `ns_` prefix, and the directory tree is the program map:
```text
baml_src/
├── ns_catalog/
│ └── product.baml
└── ns_orders/
└── order.baml
```
There are no imports. Definitions inside a namespace are available throughout that namespace, while definitions in another namespace are addressed by one fully qualified name:
```baml
function line_item() -> root.catalog.Product {
root.catalog.Product { name: "Keyboard" }
}
```
Classes are always constructed with their names—`Product { ... }`—rather than anonymous record syntax.
Design proposal: [BEP-008](https://beps.boundaryml.com/beps/08).
### `baml describe`: AST-aware discovery
`baml describe` is easier for an agent to use than an LSP and more informative than grep. It returns the resolved definition, dependencies, and call sites without making the agent read whole files into context.
```text
$ baml describe greet
function greet baml_src/main.baml:5-7
function greet(name: string) -> Greeting {
Greeting { message: "hi, " + name }
}
dependencies:
class Greeting baml_src/main.baml:1
references (2):
baml_src/main.baml:10 greet("world").message
baml_src/main.baml:18 assert.equal(greet("bob")…
```
The resolved reference list is especially useful: an agent can see every call site and spot near-duplicate implementations before writing another copy.
### `baml run