ilo 0.11.2

ilo — a programming language for AI agents
Documentation
# The ilo Manifesto

## The Audience Is Not Human

Every programming language in use today was designed for people. The syntax, the error messages, the tooling — all optimised for a brain that reads left-to-right, tracks visual indentation, and cares about aesthetics.

AI agents are not that brain. They produce tokens sequentially. They consume tokens from a finite context window. Every token they spend — generating, reading, retrying — costs real time and real money.

ilo is designed for them.

## The Only Metric

**Total tokens from intent to working code.**

```
Total cost = spec loading + generation + context loading + error feedback + retries
```

Every design decision is evaluated against this number. If a feature reduces it, it's in. If it increases it, it's out. No exceptions for elegance, readability, or convention. Note: until agents are trained on ilo, spec clarity is itself a token cost — a confusing spec means more retries. Some decisions that look like "readability" concessions are actually optimising the spec-loading term.

## The Five Principles

### 1. Token-Conservative

The north star. Every choice evaluated against total token cost across the full loop — not just "short syntax," but including retries, error feedback, and context loading.

A named argument like `amount: 42` costs more tokens than positional `42`. We initially worried positional args would cause parameter-swap errors — but across 10 syntax variants and 4 task types, positional args scored 10/10 generation accuracy. The swap concern was unfounded. Positional args are the single biggest token saver.

**What the agent cares about:** "How many tokens will this cost me end-to-end?"
**How this helps:** The language is as terse as possible *without increasing retry rate*. Where there's a tradeoff between generation cost and error rate, we optimise for total cost.

**Prefix notation** eliminates parentheses and saves tokens at every nesting level. `(a * b) + c` becomes `+*a b c` — 4 fewer characters, 1 fewer token. Deeper nesting saves more: `((a + b) * c) >= 100` becomes `>=*+a b c 100` — 7 fewer characters, 3 fewer tokens. Across 25 expression patterns, prefix notation saves 22% of tokens and 42% of characters vs infix. See the [prefix-vs-infix benchmark](research/explorations/prefix-vs-infix/) for the full analysis.

**Guards instead of if/else** eliminate nesting depth. In a traditional language, conditional logic stacks:

```python
if a:
    if b:
        if c:
            return x
```

Each level adds indentation, a closing brace, and more state for the agent to track. In ilo, guards are flat statements that return early and chain vertically:

```
>=a 0 x   -- if a >= 0, return x; otherwise continue
>=b 0 y   -- if b >= 0, return y; otherwise continue
z         -- default return
```

No nesting. No closing braces to match. Each guard is a single statement the agent can emit and forget. Depth stays constant regardless of how many conditions there are.

**Match instead of switch** eliminates fall-through — a common source of bugs in C-style languages where missing a `break` causes execution to bleed into the next case. In ilo, each match arm is independent and exhaustive. There is no fall-through because there is no execution path between arms.

**Naming rule:** prefer single-word identifiers. Across all major LLM tokenisers (OpenAI, Anthropic), common English words are 1 token. Hyphenated compounds are always 2 — the hyphen forces a token split. Every hyphen in a name doubles its token cost. Abbreviations (`uid` vs `user`) save characters but not tokens — tokenisers encode common words as single tokens either way. Both styles score 10/10 in generation accuracy.

### 2. Constrained

Small vocabulary. Closed world. Few ways to do things.

When an agent generates the next token, how many valid options are there? Fewer valid next-tokens means fewer wrong choices means fewer retries. This isn't about limiting expressiveness — it's about making the right token obvious.

- **Closed world.** Every callable function is known ahead of time. The agent cannot hallucinate an API that doesn't exist.
- **Small vocabulary.** Fewer keywords, fewer constructs, one way to define a function, one way to call it, one way to handle errors. Where multiple forms exist (braceless guards vs braced guards, prefix ternary vs braced ternary, short aliases vs long aliases), each serves a different context — inline vs file, simple vs complex — rather than offering interchangeable alternatives.
- **Verification before execution.** All calls resolve, all types align, all dependencies exist — checked before running anything.

**What the agent cares about:** "At each generation step, how many valid tokens are there?"
**How this helps:** The language becomes a set of rails. Constrained generation can feed valid next-token sets back to the agent, making it *impossible* to generate invalid code.

### 3. Self-Contained

Each unit carries its own context: deps, types, rules.

An agent working on function A shouldn't need to load functions B through Z to understand what A does. The less context required per step, the fewer tokens consumed, the more of the context window is available for the actual task.

- **Explicit dependencies.** Each function declares exactly what it needs — by name, with types. No globals, no ambient state, no implicit imports. I/O boundaries (`env`, `rd`, `now`) are the deliberate exception — they access the outside world, but the program itself has no mutable shared state.
- **Small units.** A function that fits in a few dozen tokens can be loaded, understood, and modified cheaply.
- **Spec as context.** Until foundation models are trained on ilo, agents need the spec somewhere they can access it — bundled with the program, fetched on demand, or installed locally.

**What the agent cares about:** "How much context do I need to load to work on this unit?"
**How this helps:** Minimal context loading per task. Each unit is self-describing. The agent never needs to hunt for definitions elsewhere.

### 4. Language-Agnostic

Minimise dependency on English or any natural language.

Early variants used short English-derived keywords (`fn`, `let`, `match`, `for`, `if`). Experiments showed structural tokens outperform keywords entirely — the winning syntax (idea8/idea9) replaced control-flow keywords with single-character sigils:

- `?` conditional/match, `!` auto-unwrap, `~` ok-wrap, `^` err/throw, `@` iterate, `>` pipe/return
- Control-flow keywords reduced to abbreviations: `wh`, `ret`, `brk`, `cnt`
- Builtins are short abbreviated names: `len`, `hd`, `tl`, `map`, `flt`, `fld`, `srt`, `cat`, `spl`, `rd`, `wr`, `env`, `prnt`, `fmt`, `str`, `num`, etc. These read as domain vocabulary, not English prose — an agent learns them as fixed tokens from the spec, not from natural-language understanding.
- Remaining keywords: `type`, `tool`, `with`, `use`, `nil`, `true`, `false`, plus type sigils `L`, `R`, `O`, `M`, `S`, `F`
- Agents learned the full vocabulary from spec + examples with 10/10 accuracy

Sigils won for control flow because they are unambiguous single tokens that cannot be confused with variable names or hallucinated into natural-language variations. Builtins use short names rather than sigils because there are too many to encode as single characters — but they are a closed, memorisable set, not open-ended English.

**What the agent cares about:** "Can I learn this language from its spec and examples, regardless of my training?"
**How this helps:** The spec is small enough to bundle with any program. Keywords are learned from structure, not from natural language understanding.

### 5. Graph-Reducible

When analysing code, reduce context size by loading only the relevant subgraph.

Writing code costs the same tokens regardless of program size. But *reading* code — understanding what exists, what calls what, what breaks if you change something — scales with program size. In a traditional language, the agent loads entire files or entire repos to answer simple questions. ilo makes the dependency graph explicit and queryable, so the agent loads only what it needs.

- **Typed signatures as contracts.** Every function declares its params and return type. To understand what `create-user` does, an agent only needs its signature — not the 20 other functions in the file. Signatures are the graph's edges.
- **Explicit dependencies.** `use "auth.ilo" [vld-email vld-plan]` declares exactly what's imported. No scanning, no guessing. The call graph is derivable from the AST without execution.
- **Queryable structure.** `ilo graph` extracts call graphs, type dependencies, reverse callers, and transitive subgraphs as JSON. An agent modifying one function in a 30-function program loads 6-10% of the code instead of 100%.

**What the agent cares about:** "How much do I need to read before I can write?"
**How this helps:** The agent loads the target function's source, its dependencies' signatures, and the types it references — nothing more. As programs grow, the savings compound: the subgraph stays small even as the program gets large.

## Principles We Considered and Dropped

**Deterministic** — falls out naturally from constrained + self-contained. An agent doesn't think about determinism; it thinks "did this work?" If the language is constrained and self-contained, determinism follows.

**Append-only** — solved by small self-contained units. If units are small enough, regenerating them is cheap and safe. No need for a structural constraint.

**Immediate feedback** — a property of the runtime/tooling, not the language itself. Important for the ecosystem, but not a language principle.

## The Name

*ilo* is Toki Pona for "tool" ([sona.pona.la/wiki/ilo](https://sona.pona.la/wiki/ilo)).

Toki Pona is a constructed language built around radical minimalism. ~120 words. 14 phonemes. Complex ideas expressed by combining simple terms. It constrains human expression to force clarity of thought.

ilo does the same for machine programmers. A minimal, verified vocabulary. Complex programs built by composing small, self-contained units. The constraint is the feature.

## What ilo Is Not

**Not a framework for building AI agents.** There are plenty of those. ilo is a language for agents to write programs *in*.

**Not optimised for human readability.** Humans can read it — it's not obfuscated — but no decision is made because it "looks cleaner" or "reads more naturally." If a design is uglier but costs fewer total tokens, it wins. Newlines, indentation, and multi-line comments are human concerns — agents don't need them. An entire ilo program can be one line. The formatter provides expanded output (`--expanded` / `-e`) when humans need to review.

**Not theoretical.** Every principle here addresses measured failure modes in AI-generated code: hallucinated APIs, context window exhaustion, wasted retry cycles from vague errors.

## What ilo Is

A **minimal, verified action space** — the smallest set of constructs an agent needs to express computational intent, with relationships made explicit and everything else stripped away.

---

Read this on the docs site: [ilo-lang.ai/docs/manifesto](https://ilo-lang.ai/docs/manifesto/)