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./helloHello, 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, swap
  • std::ptr — null, read, write, copy
  • std::cmp — ordering, comparison
  • std::hash — hashing infrastructure
  • std::iter — iterator traits
  • std::vec — dynamic vector
  • std::string — UTF-8 strings
  • std::option — Option<T>
  • std::result — Result<T, E>

Tier 2 — Systems

  • std::fs — filesystem ops
  • std::path — path manipulation
  • std::net — networking
  • std::sync — atomics, synchronization
  • std::io — input/output streams

Tier 3 — Advanced

  • std::char — character predicates
  • std::time — Duration
  • std::process — Command
  • std::thread — threading
  • std::collections — collections
  • std::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

Download v1.0.1 Playground GitHub

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."