Hello, Axle
// hello.axle
fn main() : i32 {
println("Hello, Axle!");
return 0;
} ./target/release/axle build hello.axle -o hello
./hello
# Hello, Axle! The commands on this page call the from-source binary by its path
(./target/release/axle). If you installed axle from apt or the
Windows MSI, it is already on your PATH — drop the prefix and run axle build hello.axle -o hello.
Four things to read off that program:
fn main() : i32— every program starts atmain. The: i32return type is the process exit code:0means success, non-zero signals an error to the shell. (Theaxle newscaffold usesfn main() : voidinstead — a program that always succeeds.)println(...)— prints a line to standard output. It is part of the prelude, so it needs nouse. The explicit formConsole::println(...)calls the same builtin;Consoleis one of the handful of classes that are always in scope. (Theaxle newscaffold writes the explicit form.) Declaring your ownfn println(...)shadows the prelude word — your function is what a bareprintln(...)calls from then on.::reaches a namespace member — a static method on a class (Console::println), a module function (math::pi()), an enum variant (Color::Red)..is for instance members. More on that in the tour.- No
free, no garbage collector. The compiler decides where each value lives and inserts its cleanup for you — see the Memory model.
Tip —
axle run hello.axlebuilds a native binary and executes it in one step, as it does inside a project directory;axle buildis the same build with the binary kept.
Inspect what the compiler produced
# LLVM IR (post-optimiser when `-O > 0`)
./target/release/axle build hello.axle -O 2 --emit llvm -o hello.ll
# Native assembly
./target/release/axle build hello.axle -O 2 --emit asm -o hello.s
# Intermediate-representation debug dumps (provisional shape)
./target/release/axle build hello.axle --emit hir -o hello.hir
./target/release/axle build hello.axle --emit ast -o hello.ast A slightly bigger example
use std::collections::ArrayList;
fn main() : i32 {
let squares : ArrayList<i32> = new ArrayList<i32>();
for (i of 0..10) {
squares.add(i * i); // 0, 1, 4, 9, …, 81
}
let sum : i32 = 0;
for (j of 0..squares.size()) {
sum = sum + (squares.get(j) ?? 0); // in range — never null
}
println(sum); // 285
return 0;
} for (i of 0..n) walks a range — the idiomatic loop in Axle. Index a
collection over 0..xs.size() rather than iterating it directly.
println is one prelude builtin, not a family of overloads. It is
variadic, and the compiler picks the format from the argument’s static
type at the call site — so there is no per-type variant name to remember.
The types it accepts: bool, char, string, every sized integer
(i8–i64, u8–u64), f32 / f64, a class or Shared<T> reference
(printed as its address), and a T | null of one of those.
ArrayList lives in std::collections — see the API reference for the
complete method list.
Model data with enum and match
A closed set of cases is an enum; match selects an arm and is
itself an expression, so it returns directly. The compiler checks
that every variant is covered — a missing case is a compile error,
not a runtime surprise:
enum Color { Red, Green, Blue }
fn name(c : Color) : string {
return match c {
Color::Red => "red",
Color::Green => "green",
Color::Blue => "blue",
};
}
fn main() : i32 {
println(name(Color::Green)); // green
return 0;
} Both constructs have a dedicated chapter: Enums covers variant payloads, backing types and enum methods, and Pattern matching covers every pattern
kind and the exhaustiveness rule. The rest of the type-system surface — struct value types, trait contracts, generics and @derive — is in
the Language tour and Generics and traits.
Limitations
printlndoes not take astructvalue. A value-typestructhas no static typeprintlnknows how to print, and codegen stops with an internal error rather than a diagnostic. Print the fields —p.x, or atoStringyour class provides — instead.- A class prints as its address.
println(obj)is a pointer, not a rendering; give the class atoStringand print that when the address is not what you meant. for (v of xs)wants a range or an array. A collection is walked by index:for (j of 0..squares.size()). The loop head over anything else isE0517.
See also
- Language tour — the surface syntax in one pass.
- Memory model — what
new T(...)actually does. - Enums and Pattern matching — the two constructs from the last example, in depth.
- Recipes — task-oriented examples (collections, I/O, processes, …).
- Concept index — every concept on one page with a link to where it’s documented.