Introduction
Hale is an LLVM-compiled systems and service programming language designed around hierarchical arena ownership, actor-like components (locus), typed message channels (topic), and compile-time architectural verification (claims).
Hale compiles directly to native machine code (or WebAssembly) with no garbage collector, no borrow checker, and no shared mutable state across threads:
- Single-tree memory ownership: Every stateful component is a
locusthat owns a contiguous memory arena. Child loci allocate from their parent’s arena and free their entire region inO(1)time when they dissolve. - Data-race-free concurrency: Loci communicate either vertically (parent calling child methods) or laterally via a typed pub/sub message bus (
topic). Payloads are copied into the receiving locus’s arena or passed across zero-copy shared-memory rings (shm_ring). - Deployment separated from logic: Thread assignment (
placement), cross-process transports (bindings), and architectural invariants (claims) are declared on the entrypoint (main locusorfleet locus) rather than hardcoded inside components. - Whole-program static verification: Because component ownership, message routing, and side effects are first-class syntax,
hale checkderives a complete graph of the program (ApplicationModel) and checks reachability laws, effect budgets, and memory bounds at compile time.
Interactive Playground: Run and edit Hale programs directly in your browser at play.hale-lang.org, or browse runnable examples in the example gallery.
Mental Model for Experienced Programmers
Section titled “Mental Model for Experienced Programmers”| If you know… | How Hale compares |
|---|---|
| Go | Replace goroutines + channels + mutexes with locus + topic. Thread pools (cooperative, pinned, where async_io) are configured once in main’s placement { } block. Errors use fallible(E) + or instead of if err != nil. |
| Rust | Memory is managed by hierarchical arenas tied to locus lifetimes (birth → run → dissolve) and per-call scratch regions, avoiding lifetime annotations and borrow checking while remaining GC-free. |
| Erlang / Elixir (OTP) | A locus is a statically typed process/actor with built-in supervision (closure invariants + on_failure { restart; } policies) and native C-speed execution. |
| TypeScript / Python | Simple scripts start with a 3-line fn main(), first-class Decimal and Duration literals, built-in JSON/HTTP/SQLite/Postgres support, and type inference on local bindings. |
Core Language Vocabulary
Section titled “Core Language Vocabulary”Every Hale program is built from a small, orthogonal set of declarations:
| Construct | Purpose | Equivalent Concept |
|---|---|---|
type |
Pure value record or union passed by value; has fields and methods, no lifecycle | struct / enum / data class |
locus |
Stateful component with an arena, params, methods, lifecycle hooks, and bus wiring |
Actor / service / state machine |
@form(...) |
Compiler-specialized data structure (vec, hashmap, set, ring_buffer, lru_cache, heap) backed by a locus’s arena |
Vec<T>, HashMap<K,V>, BTreeSet<T> |
topic |
Named, typed pub/sub channel (Topic <- msg;, subscribe Topic as handler;) |
Typed Kafka/NATS subject or Go channel |
seam |
Linear typed pipeline routing a set of message types across loci and methods | Typed middleware / railway pipeline |
@repo(T) |
Locus managing keyed persistence (get, create, put, remove) with state effect tracking |
Repository / DAO with compile-time CRUD effects |
api |
Typed RPC/streaming contract served over HTTP, REST, Unix sockets, gRPC, MCP, or WebSockets | Protobuf / OpenAPI service definition |
fallible(E) |
Value-level error channel on a function signature, handled at the call site with or |
Result<T, E> with mandatory handling |
closure & on_failure |
Runtime state invariant on a locus + parent supervision policy (restart, bubble) |
OTP supervisor strategy |
@effects & @budget |
Transitive compile-time bounds on syscalls, blocking, allocations, and domain effects | Static capability / effect system |
claims & constitution |
Compile-time architectural assertions (isolation, gating, cardinality) over the program graph | Zero-cost static architecture tests |
main locus |
Process entrypoint specifying child placement, transport bindings, and claims |
main() + thread & transport wiring |
fleet locus |
Multi-binary composition specifying host placement, cross-process bindings, and fleet claims | Type-checked Docker Compose / K8s manifest |
Code Tour: From Script to Service
Section titled “Code Tour: From Script to Service”A minimal Hale program requires only fn main():
fn main() { let unit_price = 19.99d; let total = unit_price * 3.0d; println("Total: $", total);}When your program needs long-lived state and reactive messaging, you define data with type, channels with topic, and components with locus:
type Player { id: String; name: String; }type MatchInfo { match_id: String; size: Int; }
topic JoinQueue { payload: Player; }topic MatchReady { payload: MatchInfo; }
locus Matchmaker { params { target_size: Int = 4; queued: Int = 0; } bus { subscribe JoinQueue as on_join; publish MatchReady; }
fn on_join(p: Player) { self.queued = self.queued + 1; if self.queued >= self.target_size { MatchReady <- MatchInfo { match_id: p.id, size: self.queued }; self.queued = 0; } }}Matchmaker does not know whether JoinQueue arrives from an in-memory queue, a Unix domain socket, a shared-memory ring buffer, or NATS JetStream, nor which CPU core or thread pool it runs on. Those decisions live in main locus, so the component itself stays portable across single-binary tools, multi-process daemons, and WebAssembly targets.
Documentation Map
Section titled “Documentation Map”- Part I: Language & Standard Library — Primitive types,
Decimalmoney andDurationtime, physical units, functions, control flow,fallible(E)error handling, records,@formcollections, and standard library packages (files, JSON, HTTP, sockets, crypto, CLI, logging, metrics, testing). - Part II: Concurrency, Services & APIs — Locus lifecycle (
birth,run,drain,dissolve), the typed bus, thread placement (cooperative,pinned,async_io), parent-child supervision, seams,@repo(T)repositories,apisurfaces (RPC, REST, MCP, gRPC, WebSockets), and multi-binaryfleet locusdeployments. - Part III: Static Verification & Architecture Laws — Effect annotations (
@effects,@no_syscall,@no_block,@deterministic), whole-programclaims, and reusableconstitutionrules. - Part IV: Systems Programming & Runtime Internals — Arena memory layouts, zero-allocation hot paths (
@hot,@budget), zero-copyshm_ringmessaging, C FFI (@ffi("c")), WebAssembly, live topology inspection (hale iris), and deterministic replay (hale replay). - Part V: DNA (Autonomous & Governed Applications) — Building self-governing applications with durable PostgreSQL + Git state, NATS event routing, Prometheus health reflexes, Vault secret confinement, and sandboxed agent/human task workflows.
- Part VI: Multi-Record Control Plane (Habitat) — Managing local and remote multi-service environments with
hale habitat.
Next Steps
Section titled “Next Steps”- Install the Toolchain — Download a prebuilt binary or build from source.
- Quickstart & Toolchain — Compile and run your first
.hlprogram and explore the CLI. - Tutorial: Build a Job Queue — Evolve a 10-line script into a multi-process service in four steps.