Xo is experimental. The source is now public on GitHub, or try Xo in your browser.
v0.32 · experimental · builds for Linux, Mac, Windows, WASI

Agents write the code. Xo catches what they miss.

Xo (pronounced ex-oh) is a small, statically typed language for backends, tools, and web services. A human project owner oversees AI that builds and maintains the language. Things like ignored errors, nil, missed cases, and hidden side effects are compile errors in Xo. The compiler is optimized to return helpful information back to the agent (or human).

Finan the fennec fox, alert
hello/main.xo
fn greet(name: Str) -> Str {
  "Hello, ${name}!"
}

fn main(os: Os) {
  os.stdio.println(greet("world"))
}

test "greets by name" {
  expect(greet("Xo")) == "Hello, Xo!"
}
zsh
$ xo run .
Hello, world!
$ xo test .
ok: 1 passed, 0 failed, 0 skipped

See a web service →

// why xo

Ideas from great languages, in one small one.

Xo borrows what has proven itself in Go, Rust, Swift, and Kotlin, and turns the bugs tests usually miss into compile errors. In a study of 30 planted safety bugs, Xo caught or prevented 23 to 29, against 13 for Go with strict analyzers and 9 for plain Go.

from Go

Small and fast

Few keywords, one obvious way, one formatter, fast builds, and static binaries for Linux, Mac, Windows, and WASI.

from Rust

Errors you can't ignore

Result and ?; an unhandled error is a compile error.

from Rust and ML

Sum types, exhaustive match

Every case handled, and every match target.os arm type checked for every target.

from Swift and Kotlin

No nil

Optionals are T? and checked at every use.

from Swift and Kotlin

Structured concurrency

No task outlives its scope; value semantics mean no data races.

new in Xo

Effects you can see

uses net, fs.read in the signature, with capabilities passed from main.

interop · Go

Use any Go package

To help with adoption, Go libraries can be used with Xo until its own library support grows. Bindings are generated on the fly. (T, error) becomes T ! go.Error, and a nil pointer becomes T?.

use go "regexp"
use go "github.com/google/uuid" as uuid
interop · C

Call C from native builds

Use C libraries the way Go programs do with cgo: the LLVM and arm64 backends bind C headers directly, with effects declared at the boundary.

use c "zlib.h" link "z" uses none
// zlib.crc32(0, b"hello")
// learn

Learn Xo in twelve short steps.

Every program in the tour checks, runs, and passes its tests with the current toolchain, and every error shown is the compiler's real output.

  1. 01BasicsBindings, functions, string interpolation, and expressions.
  2. 02Structs, enums, and newtypesStructs, sum types, derived behavior, type aliases, and newtypes.
  3. 03Lists, maps, and setsImmutable persistent collections, higher order methods, and safe map reads.
  4. 04No nil: optionalsT? instead of nil, if let, ??, and ? on optionals.
  5. 05Errors you have to handleResult types, ?, error enums with from variants, and why ignoring an error does not compile.
  6. 06Pattern matchingExhaustive match, ranges, alternatives, list shapes, tuples, and is.
  7. 07Values and mutationValue semantics, var receivers, updating collections in place, Mutex and Atomic.
  8. 08Effects and capabilitiesuses clauses, capability values passed from main, and fake capabilities in tests.
  9. 09Structured concurrencyScopes, tasks, await, channels, and cancellation without ctx plumbing.
  10. 10Contracts and testsrequires, ensures, invariants, test blocks, property tests, and contract fuzzing.
  11. 11A small web servicestd/http routing, JSON, typed path parameters, errors that map to status codes, and in process tests.
  12. 12Using Go packagesuse go to call any Go package, with bindings generated automatically.

From the blog

// supply chain

A dependency can only do what you let it.

Supply chain attacks are on the rise, so Xo's effects give you control over what each dependency can do. Put a ceiling on a dependency in xo.mod. If an upgrade starts doing more, the build fails at your call, before the new code ever runs. No mainstream language checks limits per dependency in the compiler: Go and Rust rely on audits and analysis tools, Deno and Node grant permissions per process, and Java's SecurityManager is disabled as of JDK 24.

an upgrade that reaches into Go
$ cat xo.mod
module example.com/shop
edition preview

require github.com/acme/billing v1.4.1 uses none
$ xo check .
main/main.xo:4:6: error XO1309: `charge` of github.com/acme/billing v1.4.1
  performs `go`, above the effect ceiling `uses none` of that dependency in xo.mod
  note: xo.mod:4:1: the ceiling is declared here
  note: review why github.com/acme/billing needs `go`; then raise the ceiling,
  or require a version within it (decision 0086)
at compile time

What code may do

  • Authority is a value: a library gets the network, files, or processes only if handed them.
  • Every pub function declares its effects, checked against its body; logging is the log effect.
  • A ceiling per dependency in xo.mod (XO1309).
  • A dependency's Go and C code count only with your own vouch (XO1316).
at update

What changed

  • xo.lock records each dependency's effects; an update that reaches further fails until you accept it (XO1317).
  • xo get prints every API and effect change.
  • xo publish rejects a minor or patch release that adds an effect (XO1310).
  • Nothing runs at install: no hooks, no build scripts, no code on import.
at run time

Less than the whole thing

  • os.net.only(["api.example.com"]) reaches only those hosts; anything else is Denied before it happens.
  • Env.only, Proc.only, and Fs.sub narrow variables, commands, and directories.
  • task.with_budget caps the steps and running time of a call, so a dependency that loops forever is stopped.
  • xo audit reports every dependency's effects, escape hatches, log call sites, and the capabilities you pass it.

Measured: of 17 attack scenarios from the Backstabber's Knife Collection taxonomy, rebuilt as malicious Xo dependencies, Xo blocks 12, four of them with no setup. Hardening is opt-in, a vouch is your own review, and a wrong answer performs no effect, so tests remain the defense there; an endless loop is stopped only under a budget. How to use each one, and the full results.

// how it builds

One language, native by default.

Xo has five backends. Its own arm64 and x86-64 backends, assemblers, and linkers are written in Xo and need no C toolchain. The native runtime is written in Xo, with no C, and frees memory by reference counting, with no garbage collector; a function marked uses realtime gets bounded loops, no recursion, and no allocation. Debuggers see your variables, structs, lists, and maps, and a profiler and an execution trace are built in. The Go backend is the other way to build the same program, and the WebAssembly backend runs it over WASI. Interop differs by backend: native builds can call C with use c, and the Go backend can call Go. On the network side Xo's servers match or beat Go's on the rows measured: HTTP/2 with 100 streams runs 2.1 times Go's rate, bulk TLS 1.17 to 1.37 times, and the HTTP/1.1 servers use 0.39 to 0.63 of Go's CPU per request (medians, a loaded machine). Against Go on the benchmarks measured for each release, 0.32.0 runs 14 of 20 workloads faster and 4 within noise, uses less peak memory on all 20, and builds smaller binaries (1.7 MB against 2.4 MB); the 0.32 notes list the two it is still slower on.

native · default on arm64

arm64 backend

Fast builds on Apple silicon Macs and arm64 Linux, static Linux binaries from a Mac, and binaries smaller than LLVM's.

xo build
native · default on x86-64

x86-64 backend

Fast builds on x86-64 Linux, Intel Macs, and Windows, built the same way as arm64's, with no C toolchain. Within about 6 percent of LLVM's instructions on our benchmarks, and closing (how).

xo build
native · optional

LLVM backend

Builds optimized native code with clang, for every native target, Windows included, when you want clang's optimizer.

xo build --backend=llvm
reference

Go backend

Writes Go source that points back to your .xo lines, for every Go target, with Go's garbage collector, and can use go any Go package.

xo build --backend=go
experimental

WebAssembly backend

WebAssembly that runs over WASI with its own runtime. It builds the compiler that runs the playground; a program it cannot build yet stops with a clear error, never with wrong output.

xo build --backend=wasm
all five

Same program, same output

Every backend is tested against the others: the test programs and randomly generated ones must print the same output on each, and every change to the compiler must rebuild it byte for byte.

// get xo

Download it, or build it from source.

The latest release has archives for macOS and Linux (arm64 and x86-64) and Windows (x86-64): unpack one and put its bin on your PATH. Native programs build with no C toolchain on arm64 and x86-64 hosts. To build from source instead: the compiler is written in Xo, so any xo builds the next one, and with none at all the first build starts from Xo's last Go version and walks forward to today's; that needs git and Go 1.26 or later, once (and clang on x86-64), and took 25 to 37 minutes on a 16-core machine.

zsh
$ curl -LO https://github.com/xo-lang/xo/releases/download/v0.32.0/xo-v0.32.0-darwin-arm64.tar.gz
$ tar -xzf xo-v0.32.0-darwin-arm64.tar.gz
$ export PATH="$PWD/xo-v0.32.0-darwin-arm64/bin:$PATH"
$ xo init example.com/hello
// one binary

A toolchain built for agents to drive.

Checks, builds, and queries answer in JSON. Lookups and edits work on resolved symbols, so an agent never guesses from text, and every edit is rechecked before it is written. dbg(x) prints a value from any function, pure ones included, on every backend, and a release build strips it. And when the check is clean, Finan sleeps soundly.

xo check --jsonType, effect, and concurrency checks
xo def NAMEWhere a symbol is defined
xo refs NAMEEvery use of a symbol
xo doc NAMESignature, docs, and methods
xo edit renameEdits rechecked before writing
xo test --explore=NSearches task interleavings
xo vet --costCosts the source does not show
xo explain XO0401Every code, explained
zsh
$ xo check ./...
1 module(s), 0 error(s), 0 warning(s)
$ xo build --targets=all .
# static binaries for Linux, Mac, Windows, WASI
Finan curled up asleep
Our mascot, Finan the fennec fox, sleeps soundly.