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);
}
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, CPU time, wall-clock and binary size across -O0…-O3 — straight from axle bench.

Benchmarks for v0.8.6

Strings

append

StringBuilder append throughput — building one large string.

VariantRunCompileOutput
Run timeCPU timeRAMCompile timeRAMBinary
Axle -O05 ms5 ms29.7 MB84 ms117.4 MB330 KB
Axle -O15 ms5 ms29.7 MB81 ms119.9 MB330 KB
Axle -O25 ms5 ms29.7 MB91 ms117.8 MB329 KB
Axle -O35 ms4 ms29.7 MB91 ms119.1 MB329 KB
Rust -O35 ms5 ms29.7 MB124 ms145.8 MB3788 KB
Rust -O26 ms6 ms29.7 MB126 ms144.4 MB3788 KB
Rust -O18 ms8 ms29.7 MB107 ms140.3 MB3788 KB
C++ -O213 ms13 ms34.9 MB499 ms120.8 MB17 KB
Rust -O013 ms13 ms29.7 MB101 ms141.7 MB3791 KB
C++ -O314 ms14 ms34.7 MB519 ms120.9 MB17 KB
C++ -O115 ms15 ms35.0 MB482 ms120.3 MB17 KB
C++ -O051 ms51 ms34.9 MB470 ms117.0 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 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.
    Console::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.