Uncompromising systems language
Pre-release. Three emitters β native via Zig, JavaScript, and C#; the playground below is the full pipeline running client-side.
import std/io
// A tor declares its exits up front
tor parse { input: string }
| ast i32 // the success exit
| message string // the failure exit
parse => ast 42
// Every exit must be handled - drop an arm and it will not compile
parse(input: "1 + 2")
| ast a |> std/io:print.ln("parsed {{ a:d }}")
| message m |> std/io:print.ln("error: {{ m:s }}")The compiler generates the specialized code; you write the readable version. State the intent, get the unrolled native loop.
64,513 vs 48,123 sieve passes/5s against the strongest Rust entry β same box, interleaved.
Every exit must be handled, every obligation discharged, impurity tracked to its boundary β or the program does not compile.
Refusals, not lints β the compiler names the dischargers it cannot choose between.
Diagnostics that teach: a refusal names what to write instead. Bounded tors and flows give both humans and agents contracts to reason against.
error[KORU030]: Resource 'tx' <in-transaction!> has multiple discharge options: rollback, commit. Discharge explicitly.
The compiler pipeline is a subflow your program can override β analysis, passes, emission. The default is a baseline, not a law.
std/compiler:coordinate is [comptime|abstract]. Verified at koru_std/compiler.kz:799.
Two interactive playgrounds β no local toolchain required.
A tor declares its named exits upfront; a caller handles all of them. No hidden
exceptions, no unhandled cases. A tor with a single output skips the ceremony and returns
through -> T instead.
import std/io
// A tor declares its exits up front
tor parse { input: string }
| ast i32 // the success exit
| message string // the failure exit
parse => ast 42
// Every exit must be handled - drop an arm and it will not compile
parse(input: "1 + 2")
| ast a |> std/io:print.ln("parsed {{ a:d }}")
| message m |> std/io:print.ln("error: {{ m:s }}")The ! suffix mints an obligation; a
parameter written <!state> discharges it. Leave a transaction open and the program refuses to compile β and when
exactly one tor can settle the resource, the compiler inserts the call itself. All of it
at zero runtime cost.
import std/io
// Connect - mints the obligation
pub tor connect { url: string } -> string<connected!>
connect -> url
// Begin - requires a live connection, mints a second obligation
pub tor begin { conn: string<connected> } -> string<in-transaction!>
begin -> conn
// Two ways to discharge <in-transaction!>
pub tor commit { tx: string<!in-transaction> }
commit = std/io:print.ln("commit")
pub tor rollback { tx: string<!in-transaction> }
rollback = std/io:print.ln("rollback")
// The only way to discharge <connected!>
pub tor disconnect { conn: string<!connected> }
disconnect = std/io:print.ln("disconnect")
connect(url: "postgres://localhost/app"): conn
|> begin(conn): tx
|> commit(tx)
|> disconnect(conn)Measured, not promised: error[KORU030]: Resource 'tx' <in-transaction!> has multiple discharge options:
rollback, commit. Discharge explicitly.
Observe a torβs transitions without modifying its flow. Attach logging, metrics, or
tracing as a detached layer β widen the pattern to tap(* -> *) and the whole program is instrumented.
import std/io
import std/taps
tor compute { x: i32 } -> i32
compute -> x * 2
// Observe every transition out of compute - compute itself is untouched
tap(compute -> *)
| Profile p |> std/io:print.ln("tapped {{ p.source:s }}")
compute(x: 21): r |> std/io:print.ln("{{ r:d }}")The performance claim is not "Koru is fast." It is that the program states intent and the
compiler emits the specialized machine code for it. std/field:mark-multiples is a [transform]: each call site gets an
unrolled, residue-specialized marker with the bit-masks baked in β the same machinery that
compiles a regex to a DFA.
The 35-line prime sieve β re-measured 2026-10-10 on a dedicated Xeon 8358, interleaved against the strongest Rust entry in the field: 64,513 vs 48,123 passes/5s, correctness-pinned at Ο(10βΆ) = 78,498. The sieve, the timing loop, and the counting are all Koru; the host touches are the clock and the per-pass field allocation the benchmark's faithfulness rule demands.
Where the substrate was put under a microscope β pure codegen, no stores β the result was parity or noise. Where it wasn't, we publish that too. And where hand-written machine-level code still wins, it is one escape hatch away.
ProgramAST and Source parameters hand a
compile-time tor the program's own structure β generate code, enforce architectural rules,
or rewrite the program that called you. Typed all the way; there is no separate macro
language.
The pipeline that compiles your program β elaborate, analysis, emission β is a subflow
declared in the standard library and marked [comptime|abstract]. Your program can override it: inject passes, replace the coordinator, or drive the
pipeline for your domain. Koru compile-time is host-language runtime.
import std/compiler
// Your program can replace the pipeline that compiles it.
// The default subflow runs more passes; this override compiled and
// printed "Compiler coordination: custom pipeline".
std/compiler:coordinate = std/compiler:context-create(program_ast, allocator): c0
|> std/compiler:elaborate(ctx: c0): c1
|> std/compiler:analysis(ctx: c1)
| ctx c2 |> std/compiler:emission(ctx: c2): out => coordinated {
out.ctx.ast,
out.code,
metrics: "custom pipeline"
}
| failed f => error f.messageConditionals evaluate at runtime; the template compiles to format strings β no template engine, no allocation machinery.
import std/io
const {
debug: true
name: "Alice"
count: 42[i32]
}
// Inline templating with runtime conditionals
std/io:print.ln("{% if debug %}[DEBUG] {% endif %}User: {{ name:s }}")
// Multi-line block syntax
std/io:print.blk {
=== Report ===
{% if debug %}Mode: Debug{% endif %}
Count: {{ count:d }}
==============
}Impurity is transitive and tracked across the call graph: call an impure tor and yours is
impure. You always know which parts of a program perform effects β tracked everywhere,
and enforced wherever a tor marks itself [pure].
Host code lives in a proc and
nowhere else β an intrinsic, a syscall, a C library you have no intention of rewriting.
The seam is typed, and everything around it stays Koru.
import std/io
// Almost everything is written in Koru proper
tor double { n: u64 } -> u64
double -> n * 2
// ...until you need the metal. A proc is the seam, and the only
// place host code appears.
tor popcount { n: u64 } -> u32
proc popcount|zig { return @popCount(n); }
double(n: 21): d |> popcount(n: d): bits |> std/io:print.ln("{{ bits:d }}")The host is a target, not a foundation β and there is no runtime: no VM, no collector, no scheduler. What little support code an emit needs is emitted, in the target language, because you used it. Three emitters today: Zig for native speed and FFI reach, JavaScript β which is how the playground runs the whole pipeline in your browser β and C#.