A Tour of Zeta
v1.0.1 — The Foundational Release
Built from first principles. Self-hosting. Pure Zeta. This tour covers everything you need to start building.
Hello, Zeta
Every Zeta program starts with a main function that returns i64. Save this as hello.z:
fn main() -> i64 {
println_str("Hello, Zeta!");
return 0;
}
Compile and run with the self-hosted compiler:
./bin/zetac hello.z -o hello → ./hello → Hello, Zeta!
Binary size: ~7 kB. No runtime. No dependencies.
Variables & Types
Immutable by default. Full type inference. Explicit types optional.
let x = 42; // inferred i64
let y: f64 = 3.14; // explicit f64
let mut z = 100; // mutable i64
z += 1; // z is now 101
let name = "Zeta"; // str (string)
let flag: bool = true; // booleans
let arr: [i64; 5] = [1, 2, 3, 4, 5]; // fixed-size array
let first = arr[0]; // index access: 1
Available types: i64, f64, bool, str, [T; N] (arrays), T[N] (slices), tuples (T, U)
Functions
Named functions, single-expression bodies, and closures.
// Named function with explicit return
fn add(a: i64, b: i64) -> i64 {
return a + b;
}
// Return via last expression (tail style)
fn sub(a: i64, b: i64) -> i64 {
a - b
}
// First-class: closures capture their environment
fn make_adder(n: i64) -> (i64) -> i64 {
return fn(x: i64) -> i64 { x + n };
}
// Higher-order
fn apply(f: (i64) -> i64, x: i64) -> i64 {
return f(x);
}
let add5 = make_adder(5);
let result = apply(add5, 10); // 15
Control Flow
If, while, for loops, and match expressions.
// If expressions (can bind to a value)
let max = if a > b { a } else { b };
// While loops
let mut i = 0;
while i < 10 {
println_i64(i);
i += 1;
}
// For loops over ranges
for i in 0..10 {
println_i64(i);
}
// For loops over collections
let v = Vec::new();
v.push(1); v.push(2); v.push(3);
for item in v {
println_i64(item);
}
Match Expressions
Exhaustive pattern matching on enums with destructuring.
enum Option<T> {
Some(T),
None,
}
fn describe(v: Option<i64>) -> str {
return match v {
Option::Some(n) => if n > 0 {
"positive"
} else {
"non-positive"
},
Option::None => "nothing",
};
}
// Match as expression — the result is the matched arm's value
let desc = describe(Option::Some(42));
// desc == "positive"
Algebraic Data Types
Structs, enums, and tuples — the building blocks of data.
// Named struct
struct Point {
x: i64,
y: i64,
}
let p = Point { x: 3, y: 4 };
let dist = (p.x * p.x + p.y * p.y) as f64;
// Enum with generics
enum Result<T, E> {
Ok(T),
Err(E),
}
// Tuples
let pair = (1, "hello");
let first = pair.0; // tuple index access
Generics & Concepts
Parametric polymorphism with concept constraints and specialization.
// Generic identity function
fn identity<T>(x: T) -> T {
return x;
}
// Concept-constrained generic
fn max<T: Ord>(a: T, b: T) -> T {
if a >= b { return a; }
return b;
}
// Concept refinement hierarchy from Elements of Programming
// Regular → TotallyOrdered → Semigroup → Monoid → Group → Ring
// Concept checking at compile time
fn semigroup_op<T: Semigroup>(a: T, b: T) -> T {
return a + b; // only valid if T satisfies Semigroup
}
Memory Model
Stack-priority allocation with ownership semantics and optional heap.
// Stack-allocated by default
let arr: [i64; 5] = [1, 2, 3, 4, 5];
let sum = arr[0] + arr[1] + arr[2];
// Heap-allocated via Vec
let v = Vec::new();
v.push(10);
v.push(20);
v.push(30);
let first = v[0]; // 10
// Immutable borrow
let len = v.len();
// Mutable access (unique at runtime)
v.push(40);
No lifetimes, no borrow checker complexity. Ownership is tracked simply: one writer or many readers.
Methods & Impl Blocks
Attach methods to types with impl blocks.
struct Point { x: f64, y: f64 }
impl Point {
fn new(x: f64, y: f64) -> Point {
return Point { x, y };
}
fn magnitude(self) -> f64 {
return sqrt(self.x * self.x +
self.y * self.y);
}
}
let p = Point::new(3.0, 4.0);
let m = p.magnitude(); // 5.0
Compile-Time Evaluation (CTFE)
Execute arbitrary Zeta code during compilation with comptime blocks.
// Compute values at compile time — zero runtime cost
const FACTORIAL_10: i64 = comptime {
fn fact(n: i64) -> i64 {
if n <= 1 { return 1; }
return n * fact(n - 1);
}
fact(10)
};
// FACTORIAL_10 = 3628800 — baked into the binary
// Algebraic semiring fusion: operations fuse into single passes
const TOTAL: i64 = comptime {
let data = [1, 2, 3, 4, 5];
let mut sum = 0;
for i in data {
sum += i * 2;
}
sum // 30 at compile time
};
Standard Library
20+ modules across three tiers. All self-hosted Zeta.
Tier 1 — Core
std::mem— size_of, align_of, swapstd::ptr— null, read, write, copystd::cmp— ordering, comparisonstd::hash— hashing infrastructurestd::iter— iterator traitsstd::vec— dynamic vectorstd::string— UTF-8 stringsstd::option— Option<T>std::result— Result<T, E>
Tier 2 — Systems
std::fs— filesystem opsstd::path— path manipulationstd::net— networkingstd::sync— atomics, synchronizationstd::io— input/output streams
Tier 3 — Advanced
std::char— character predicatesstd::time— Durationstd::process— Commandstd::thread— threadingstd::collections— collectionsstd::ffi— foreign interface
Correctness Built In
Precondition and postcondition assertions — not a linter, a language feature.
fn divide(a: i64, b: i64) -> i64 {
pre(b != 0, "division by zero");
return a / b;
}
fn sqrt_checked(x: f64) -> f64 {
pre(x >= 0.0, "sqrt of negative");
let result = sqrt(x);
post(result >= 0.0, "sqrt must be non-negative");
return result;
}
// Loop invariants
let mut sum = 0;
for i in 0..n {
invariant(sum == i * (i - 1) / 2);
sum += i;
}
// Mathematical property annotations
// #[commutative], #[associative], #[identity]
WebAssembly
Pass --target wasm32 and Zeta compiles to WebAssembly. Same LLVM pipeline, different target triple.
fn main() -> i64 {
println_str("Hello from Zeta in the browser!");
return 0;
}
// Native: ./bin/zetac hello.z -o hello
// WASM: ./bin/zetac --target wasm32 hello.z -o hello.wasm
// Run: wasmtime hello.wasm
WASM modules are typically ~4 kB with zero runtime overhead. No polyfills, no JavaScript glue.
SIMD & Auto-Vectorization
Zeta's type system enables LLVM to auto-vectorize without manual hints.
fn vector_add(a: [f64; 4], b: [f64; 4]) -> [f64; 4] {
return a + b; // auto-vectorized to SIMD
}
// CacheSafe TBAA guarantees no aliasing
// LLVM generates optimal SIMD instructions automatically
// No restrict, no unsafe, no intrinsics needed
Error Messages
175+ unique error codes. Every error tells you what, where, why, and how to fix it.
// Zeta catches this at compile time:
fn main() -> i64 {
let x: i64 = "hello";
return 0;
}
// error[E2001]: type mismatch: expected i64, found str
// ┌─ src/main.z:3:21
// │
// 3 │ let x: i64 = "hello";
// │ ^^^^^^^ expected i64, found str
// │ use explicit conversion or change type
Actors & Concurrency
Lightweight actor model with channels and a work-stealing scheduler.
// Actor with channel communication
fn main() -> i64 {
let (tx, rx) = channel<str>();
spawn(fn() {
tx.send("ping");
});
let msg = rx.recv();
println_str(msg); // "ping"
return 0;
}
// 100k actor ping-pong: 0.94 ms
// 50% faster than Rust's standard channels
Murphy's Sieve
Competition-ready wheel-optimized prime counting. Baked into the language.
fn murphy_sieve(limit: i64) -> i64 {
if limit < 2 { return 0; }
// 30030-wheel: 80.8% reduction in checks
const WHEEL: i64 = 30030; // 2×3×5×7×11×13
let mut count: i64 = 1; // 2 is prime
let mut i: i64 = 3;
while i <= limit {
if is_coprime_to_wheel(i) {
if is_prime(i) { count += 1; }
}
i += 2;
}
return count;
}
Getting Started
One binary. Your code. A target triple. Nothing else.
# Clone, compile, run — that's it
git clone https://github.com/murphsicles/zeta.git
cd zeta
./bin/zetac examples/hello.z -o hello
./hello
# Self-hosting bootstrap (compile the compiler)
./bin/zetac src/main.z -o zetac
# Target WebAssembly
./bin/zetac --target wasm32 hello.z -o hello.wasm
wasmtime hello.wasm
What's Next?
You've seen what Zeta can do. Now go build something.
Dive deeper in the Documentation, read the v1.0.1 release announcement, or explore the source code on GitHub.
"The language that will outlive its bootstrap."