Because comfortable code should never cost you performance.

Low-level control,
high-level code.

Write classes and generics the way you would in Kotlin or Swift. The compiler quietly decides whether each value lives on the stack, in an arena, on the heap, or behind a refcount — and frees it for you. No garbage collector. No lifetime annotations. No ceremony.

$ curl -fsSL https://axle-lang.dev/install.sh | sh
Or download a prebuilt release
You wrote new. It cost nothing.
A real class — fields, a constructor, a method — yet escape analysis proves both points stay local and hands them plain stack slots. Zero malloc, zero free, no GC.
Why Axle

High-level code, nothing hidden.

Memory in four tiers

Every value lands on the stack, in an arena, on the heap, or behind a refcount — the compiler picks the tier for you, with no lifetimes to annotate.

Safe by construction

Double-free and use-after-free are proven impossible at compile time — the safety a borrow checker buys you, with no lifetime annotations to write.

Checked exceptions

A function’s failure set is part of its type. Catch it, or pass it up the stack — and nothing throws behind your back.

First-class SIMD

Real vector types — f64x4, f32x8 — with fused multiply-add and horizontal sums. One binary probes the CPU at startup and runs the widest instructions it has, with a safe fallback on older chips.

The compiler hand-tunes

Copy loops become memcpy, math loops auto-vectorise, tail calls become plain loops — Axle does this before LLVM even runs, handing the backend code that already arrives optimised.

Batteries you expect

Lists, maps, sets, an HTTP client, sockets, JSON — the standard library ships what C and C++ make you go find.

Side by side

The same job. Far less ceremony.

Pick a task, then pick the language you would otherwise reach for. Same outcome — Axle just asks less of you to get there.

Axle fetch.axle
use std::net::HttpClient;

fn fetch(url : string) : string ! IOException {
    return HttpClient::get(url).body();
}
long fetch(const char *url) {
    CURL *c = curl_easy_init();
    if (!c) return -1;
    curl_easy_setopt(c, CURLOPT_URL, url);
    curl_easy_perform(c);
    long code = 0;
    curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &code);
    curl_easy_cleanup(c);
    return code;
}
A third-party library, four setopt/getinfo calls, manual cleanup — for one GET.
Inside the optimiser

Safety you stop paying for.

Safe code is supposed to cost you something. A bounds check on every access. An optional wrapped around every get. A length compared a million times inside a loop you already know is fine. Axle reads the guards you wrote — a loop bound, an if (i < len), a capacity that never moves — follows them through loops and across function calls, and removes what they make redundant. You annotate nothing. You give up nothing.

One check, three costs

list.get(i) ?? 0 looks like one comparison. It is three: the optional the callee wraps its answer in, your unwrap, and the check inside. All of it exists for a case the caller usually rules out — and when it does, all three go. What is left is the load, the same one arr[i] would have emitted.

Your containers, not just ours

A program driving the standard library’s ArrayList compiles to zero bounds traps — its internal checks fall out of the invariants its own methods already maintain. The RingBuffer you wrote this morning gets the same treatment on the same terms. No blessed list of types, nothing to annotate.

And it knows when it can’t

An index read out of another array cannot be proven — nothing in the program says what that value holds. Those keep their check, and your program stays correct. The optimiser never trades a guarantee for a benchmark.

Pre-1.0, and conservative by construction: doubt always resolves to keeping the check. Where the proof isn’t there, your program runs exactly as it did.

Benchmarks

Every optimisation level, measured.

Compile- and run-time peak RAM, wall-clock and binary size across -O0…-O3 — straight from axle bench.

Benchmarks for v0.17.0

Strings

append

StringBuilder append throughput — building one large string.

VariantRunCompileOutput
Run timeRAMCompile timeRAMBinary
Rust -O27 ms43.5 MB122 ms118.1 MB3863 KB
Rust -O37 ms43.5 MB124 ms119.5 MB3863 KB
Rust -O19 ms43.5 MB112 ms116.1 MB3863 KB
Axle -O214 ms43.5 MB102 ms123.1 MB305 KB
Axle -O314 ms43.5 MB99 ms122.9 MB305 KB
Axle -O115 ms43.5 MB90 ms122.4 MB305 KB
Axle -O020 ms43.5 MB81 ms124.3 MB306 KB
C++ -O126 ms43.5 MB544 ms118.7 MB17 KB
C++ -O226 ms43.5 MB550 ms118.9 MB17 KB
C++ -O326 ms43.5 MB557 ms119.0 MB17 KB
Rust -O045 ms43.5 MB103 ms116.8 MB3869 KB
C++ -O071 ms43.5 MB523 ms115.1 MB30 KB
▸ Source
// EXPECTED_EXIT: 0
// StringBuilder benchmark #1 — raw append throughput.
// Shared logic with append.cpp / append.rs
// (run via `axle bench --compare cpp,rust`).
//
// Workload: append a fixed 16-byte ASCII chunk 1,000,000 times,
// building a 16 MB buffer (~20 capacity-doubling reallocations).
// Output: total length + 3 sampled bytes (start / middle / end) so
// the three languages cross-check byte-identically without an
// O(n^2) full-string walk.
use std::text::StringBuilder;

fn main() : i32 {
    let n : i64 = 1000000;
    let chunk : string = "abcdefghijklmnop"; // 16 ASCII bytes

    let sb : StringBuilder = new StringBuilder();
    let mut i : i64 = 0;
    while (i < n) {
        sb.append(chunk);
        i = i + 1;
    }

    // O(1) byte length as the cross-impl correctness signal (the
    // workload is deterministic, so identical length ⇒ identical
    // bytes). Reading individual bytes back via `byteAt` would be
    // O(n) per call (AxleString carries no length → `strlen`), which
    // would measure the string ABI, not the builder.
    println(sb.byteLength());
    sb.dispose();
    return 0;
}
Roadmap

Shipped is the floor, not the ceiling.

Axle is pre-1.0 and the core is solid. These land after the first stable release — each one an extension of the same idea, not a rewrite of it.

Planned

GPU compute, first-class

The same native treatment SIMD gets today — kernels the compiler checks, not a bolted-on API.

Planned

A macro system

Compile-time metaprogramming in the spirit of Rust’s, for the boilerplate no optimiser should have to see.

Planned

A dynamic runtime

Load and run .axle modules at runtime — plugins and scripting on top of the same toolchain.

Planned

WebAssembly target

A limited --target wasm mode, so the same source runs in the browser.

Planned

Annotations

Declarative metadata in the Java / TypeScript style, read by tooling and codegen alike.

Planned

Inline LLVM IR

Drop hand-written LLVM IR into a function — read and injected straight by codegen when you need the last word.

Planned

A unit-test framework

Tests as a first-class language construct, not a convention bolted on the side.

You’ve seen the tricks — now run them yourself.

Write it high-level.
Ship it native.

Pre-1.0 and moving fast. Install the toolchain and write your first program in a couple of minutes.