general-purpose · memory-safe · supervised

Build a monolith. Deploy a distributed system.

Hale collapses the linguistic layers between a domain and the machine. One primitive — the locus — with Erlang-style supervision, memory freed by structure (no GC, no borrow checker), and a compiler that enforces the design principles we've always carried as conventions.

$ curl -fsSL https://hale-lang.org/install.sh | sh
greeter.hl
locus Greeter {
    params { name: String = "world"; }
    run() { println(f"hello, {self.name}"); }
}

fn main() { Greeter { name: "hale" }; }
main.hl
main locus App {
    params {
        ingest: Ingest        = Ingest { };
        rollup: Rollup        = Rollup { };
        api:    MetricsServer  = MetricsServer { port: 9100 };
    }
    placement {
        ingest: pinned(core = 1);          // its own OS thread
        rollup: cooperative(pool = compute);
        api:    cooperative(pool = io);
    }
    bindings {
        // dev:  delete this block -> one binary, in-process bus.
        // prod: keep it -> same code, separate processes.
        Samples: unix("/run/samples.sock");
    }
}

the deployment seam

Change a block, not your code.

Placement and transport are deployment seams, not properties of your logic. With no bindings, your loci run as a single in-process binary. Bind a topic to a transport and the same source runs as separate processes — your code never changes.

one source → dev monolith → pinned threads → a cross-process fleet

Structure is the lifetime. Failure is supervised. Bad designs don't compile.

pillar 1 — one primitive

Everything is a locus.

An app, a service, a worker, a pipeline stage, a bus subscriber — one recursive shape. Pure data is a type; anything with flow is a locus. There is no third thing. Learn it once; it's the whole language.

The locus model →
counter.hl
locus Counter {
    params { n: Int = 0; }
    fn inc() { self.n = self.n + 1; }
}
handle.hl
fn handle(fd: Int) {
    let s = std::io::tcp::Stream { conn_fd: fd };
    s.send(b"ok");
    // s dissolves at scope exit:
    // fd closed, region freed. no GC, no free().
}

pillar 2 — memory you can reason about

No GC. No borrow checker.

Every locus owns a region, freed wholesale when it dissolves — deterministically, no tracing pauses, no lifetime annotations. Not “memory is free”: memory is structural and instrumentable, with built-in residency tooling for the rest.

How memory works →

pillar 3 — reliable by construction

Let it crash — supervised. And checked first.

Failures flow up to a parent's on_failure (restart / quarantine / bubble). There are no exceptions; recoverable errors are addressed inline with or. And the compiler rejects whole classes of design bugs — bus cycles, cross-thread races — at build time.

The failure model →
pool.hl
fn port(s: String) -> Int fallible(ParseError) {
    return std::str::parse_int(s) or raise;
}

locus Pool {
    accept(c: Worker) { }
    on_failure(c: Worker, err: ClosureViolation) {
        restart(c);          // let it crash, supervised
    }
}
cache.hl
locus Cache {
    // a method that hands out a managed entity:
    fn get(key: String) -> Entry {
        return self.lookup(key);
    }
}
error: method Cache.get may not return a locus value (CQRS).
  use parent-child + contract, a bus topic, or delegation.

pillar 4 — design you can't drift from

Good design, enforced.

Encapsulation. Actor isolation. CQRS. Aggregates, bounded contexts, dependency direction. The principles we've carried for decades — in books, in review comments — keep describing one underlying shape: a recursive graph of isolated state joined by typed messages. Hale makes that shape the language itself, so breaking a principle isn't a style nit — it's a compile error, like the CQRS violation on the left.

What the compiler checks →

tooling

One binary, the whole loop.

hale check answers in ~10 ms with structured, location-precise diagnostics; the LSP wires it into any editor, and build, test, fmt, doc, bench, and an MCP server all ship in the same binary. The compiler is the oracle — it says precisely what's wrong, whoever the author is.

write a locus
hale check (~10 ms)
→ structured diagnostic
→ fix → green

more in the box

The parts that make it cohere.

Closure tests

Runtime structural invariants that audit themselves at each epoch.

Projection classes

Memory strategy tuned to how many children a locus fans out to.

Structural interfaces

Satisfy an interface by shape. No impl, no ceremony.

Transport-agnostic bus

The same publish/subscribe over in-process, unix, udp, or shm.

Verification catalog

The compiler rejects bus cycles, cross-pool races, and CQRS breaks.

One seed, one binary

A directory is a program. Focused executables are cheap.

ecosystem

pond — the “non-std stdlib.”

Vendor what you need. Extensible by tier, from infrastructure to agents.

httpdbpqroutersessionsjobsmetricstracingwebsocketcryptoagent/llm
Browse pond →