Functional programming that ships a binary, not a runtime.
Algebraic data types, exhaustive matching and an effect system the compiler checks, lowered through LLVM to a native executable with no VM, no collector and no libc inside it.
The compiler is written in Axiom: 106,203 lines of it, which rebuild
themselves from a committed LLVM seed until two successive compilers are
byte-identical. And because the syntax is uniform S-expressions with a
machine-readable diagnostic surface (AXSYM, AXDL, content-derived NIDs,
--diagnostic-format=ai), it is as legible to an agent generating code as to
the person reviewing it. That was a design goal, not an afterthought.
Where this fits: if you want a small, explicit, statically-typed language that produces a self-contained binary you can reason about instruction by instruction — and you would rather have a compiler that argues with you than one that guesses — Axiom is built for you. If you want a mature ecosystem, a package registry, or green threads, it is not there yet, and Implementation status says exactly how far each piece has got.
(import IO)
; A custom effect
(effect Log
(write :: (-> String Int)))
; If the param is not satisfied with a value greater than 0 then the compiler will fail with a backtrace.
;@axiom:pre((> n 0))
(:: work (-> Int Int))
(fn (work n)
{
(write "starting")
(write "done")
n
}
)
(:: main Int)
;@axiom:effect(io)
(fn (main)
{
(handle (work 1) (Log IO) (lambda (m)
{
(println "[log] {m}")
0
}
))
(handle (work 2) (Log) (lambda (m) 0))
0
}
)
; output:
; [log] starting
; [log] donecurl -fsSL https://raw.githubusercontent.com/chrispaig3/axiom/trunk/scripts/install.sh | bash
export PATH="$HOME/.axiom/bin:$PATH"It verifies the archive's SHA-256, then builds and runs a program that
imports a standard-library module before reporting success. Archives are
published for linux-aarch64 and darwin-aarch64; on any other host it says
so and points here. linux-x86_64 is fully supported and tested — its CI leg
runs the whole battery — it just has no prebuilt archive.
Building from a checkout needs no compiler: bootstrap/ carries the
compiler's own LLVM IR, one file per target.
git clone https://github.com/chrispaig3/Axiom.git && cd Axiom
./scripts/bootstrap-from-seed.sh --install .axiom-binCONTRIBUTING § Quick Start has the prerequisites per platform and what the bootstrap is doing.
Put this in hello.ax:
(import IO)
(:: main Int)
;@axiom:effect(io)
(fn (main)
{
(println "Hello from Axiom! 🚀")
0
})axiom run hello.ax # compile and run
axiom build --input hello.ax --output hello # or keep the binary
axiom explain AX3042 # every code has a full explanation
axiom repl # Axiom 0.7.5 - REPLIO is Axiom's own standard library — that binary calls no C function, not
for printing, not for allocation. Delete the ;@axiom:effect(io) line and
the compiler tells you the claim is missing, with the path to the syscall.
For something the size of a real program, examples/
holds a batch job over a million records and the generator that writes
this repository's own API reference. Both are run by a gate in CI, which is
the only reason they still work.
--target selects the platform to generate code for, and with it both the
syscall ABI and which platform modules the standard library resolves to:
axiom --target=linux-x86_64 emit-llvm main.ax -o main.llSupported: darwin-aarch64, darwin-x86_64, freebsd-x86_64,
linux-aarch64, linux-x86_64, windows-x86_64. Defaults to the host.
A target joins that list when a CI leg executes what the compiler emits
there. That is the definition of supported in this repository, stated
here once; docs/reference.md, SECURITY.md and CONTRIBUTING.md point at
this paragraph rather than restating it, and scripts/check-doc-drift.sh
holds the copies of the list to each other and to the compiler's own
--target table.
Two standing exceptions, named rather than left quiet. darwin-x86_64
predates the rule and is executed by no runner, which is why it ships no
artifact; freebsd-aarch64 is assembled and relocation-checked but is
executed by no runner either, because an aarch64 guest is TCG-emulated on
every runner GitHub offers and the 300-minute budget was measured and
dropped. scripts/check-release-targets.sh requires that sentence to be
here: a target that is supported, ships nothing, and is tested nowhere makes
the word mean nothing, so silence about one is a gate failure.
| Language reference | The whole language, section by section |
| Examples | Worked programs — a batch job, an API-reference generator — each one pinned by a gate |
| Implementation status | What is complete, functional, or partial — with the fixture that proves each row |
| Standard library API | Every public name, generated from the source and gated against it |
| Diagnostics | AXDL, AXSYM, NID, and the agent-facing output formats |
| Memory model | The allocator, reference counting, regions, and every rule's probe |
| Error model | Result, Error, and how failure travels |
| FFI | Calling Rust, and Rust calling Axiom |
| Effects | Declaring, handling, and what the compiler checks |
| Agent harness | The tooling built on the machine-readable surface |
| CONTRIBUTING | Building, testing, the gate battery, and how to add to it |
Issues and pull requests are welcome. CONTRIBUTING.md has the project layout, how the compiler works, how to add a diagnostic or a standard-library function, and the rule every change here is held to: if you claim it, gate it.
Like what you see? ⭐ Star the repo and 🍴 fork it — a star helps other people find Axiom, and a fork is where your first contribution starts.
MIT — see LICENSE.