Baseline
A fast, safe functional language that's easy to learn and hard to break, whether you write the code or use an AI agent.
- Easy to learn: Small, consistent syntax reads the same everywhere.
- Fast and lightweight: Compiles to native code with low memory use.
- Safe by default: The compiler checks types, effects, and errors before running.
fn fetch_user!(id: Int) -> {Http, Console} Result<String, String> =
Console.print!("Fetching user ${id}")
let response = Http.get!("/users/${id}")?
match response.status
200 -> Ok(response.body)
404 -> Err("User not found")
code -> Err("HTTP ${code}") You can read this function's entire contract from the first line:
- The
!suffix on function names means they have side effects, and{Http, Console}declares exactly which ones. This function can talk to the network and print output, nothing else. - It returns
Result<String, String>instead of a bareString, so the caller knows this function can fail and they must handle the happy path and error cases. - The
?afterHttp.get!passes errors up to the caller automatically, so you don't write error-handling boilerplate in every function.
Remove {Http} from the declaration and the compiler rejects the program.
Designed for AI Agents
AI agents generate code fast but make subtle mistakes: invalid states, hidden side effects, unchecked errors. Most type systems either catch too little to help or require formal proofs that agents can't produce. Baseline finds a middle ground: types that check real constraints, like rejecting a port number of -1 at compile time, without needing formal verification expertise.
Many languages let you do the same thing in several ways. They hide side effects in functions that look safe and rely on runtime checks that can be missed. That's manageable when people review every line, but it breaks down when agents generate code at scale.
Baseline uses one syntax for each concept, so agents don't waste tokens choosing between equivalent alternatives. Effects appear in every function signature, making dependencies obvious. Constraints are checked before the code runs. The compiler gives structured JSON diagnostics that agents can parse and fix in a single pass.
Types as Specs
Refinement types let you state a constraint once. The compiler proves it at every call site. No runtime validation code to forget.
type Port = Int where self >= 1 && self <= 65535
type Percentage = Int where self >= 0 && self <= 100
type PositiveInt = Int where self > 0
fn listen(port: Port) -> Unit =
// port is guaranteed 1..65535. No validation needed.
... No null, no exceptions, no undefined. You model
your domain with algebraic types and the compiler holds you to it.
type Connection =
| Disconnected
| Connected(Socket)
| Error(String)
fn status(conn: Connection) -> String =
match conn
Disconnected -> "offline"
Connected(_) -> "online"
Error(msg) -> "error: ${msg}" Effects as Permissions
Side effects go in the type signature. If a function doesn't declare {Fs}, it can't touch the filesystem. No {Http} means no network requests. The compiler enforces this, not a linter.
// Pure: no effects, no surprises
fn add(a: Int, b: Int) -> Int = a + b
// Effectful: declares exactly what it does
fn save!(data: String) -> {Fs} Unit =
Fs.write!("out.txt", data) Built-in effects:
Console | Terminal I/O | Http | Network requests |
Fs | Filesystem | Random | Random numbers |
Env | Environment vars | Sqlite | Database |
Log | Logging (ambient) | Time | Clock (ambient) |
Want to sandbox AI-generated code? Grant {Console} but
withhold {Fs} and {Http}. The type
system is the sandbox.
One Way to Do Each Thing
There's one syntax per operation. Not a style guide, the grammar itself. One way to chain, one way to handle errors, one way to call functions.
| Concept | Baseline | Not supported |
|---|---|---|
| Chaining | x |> f |> g | method chaining, composition |
| Errors | Result<T, E> + ? | try/catch, exceptions |
| Optionals | Option<T> | null, undefined, nil |
| Calls | Module.fn(value) | value.method() |
| Negation | not x | !x |
| Concat | "${a}${b}" | a + b |
Machine-Readable Diagnostics
The compiler outputs structured JSON: source locations, error codes, and fix suggestions with confidence scores. AI agents don't need to scrape error messages, they get machine-readable diagnostics.
$ blc check app.bl --json
{
"status": "failure",
"diagnostics": [{
"code": "TYP_002",
"message": "Undefined variable `nme`",
"suggestions": [{
"description": "Did you mean `name`?",
"confidence": 0.8
}]
}]
} The agent loop: load llms.txt, generate code,
run blc check --json, apply fixes, repeat. Since there's
only one way to write each construct, the agent doesn't waste tokens
choosing between equivalent alternatives.