Learn
From a function to a concurrent web server with database transactions. Every snippet is valid U — paste any into the playground to see the C it produces.
A function is f, a name, typed parameters, and a return type.
The body is indented with tabs, and r => returns. Return type is optional for void functions.
f add(first: I, second: I) -> I
r => first + second
f greet(name: S) -> S
r => "Hello, {{name}}!"
f log_it(msg: S)
System.stderr(msg)I integer, N float, S string, L boolean, Q rational, D date, B byte. Sized variants: I8 I16 I32 I64 U8 U16 U32 U64 N32 N64.
f area(width: N, height: N) -> N r => width * height f is_even(num: I) -> L r => num % 2 == 0
[T] is a list, {K: V} is a map. Iteration uses .on() — no for loops.
Pipelines chain, and the compiler fuses them into one pass.
f even_squares(nums: [I]) -> [I] r => nums.filter(num => num % 2 == 0).map(num => num * nn) // Accumulator needs +M — locals are immutable by default f total(prices: [N]) -> N sum: N +M = 0.0 prices.on(price => ( sum = sum + price none )) r => sum
d declares a type. Fields are +M by default — the class's own methods (t) can mutate them. Mark -M for frozen-after-construction fields.
d Point x: N // +M by default — instance field y: N id: I -M // frozen after construction f move(dx: N, dy: N) t.x = t.x + dx // t = self t.y = t.y + dy f distance() -> N r => Math.sqrt(t.x * t.x + t.y * t.y)
U has no enum, no switch, no match. Instead, variants
are subtypes, and dispatch is .on() with typed handlers — the same
.on() that iterates lists. One mechanism, two uses.
d Shape color: S d Circle : Shape radius: N d Rect : Shape width: N height: N d Triangle : Shape base: N height: N
Circle, Rect, and Triangle are subtypes of Shape.
They inherit color and add their own fields. This IS the enum — each
subtype is a variant.
f area(shape: Shape) -> N shape.on( (sh: Circle) => r => 3.14159 * sh.radius * sh.radius, (sh: Rect) => r => sh.width * sh.height, (sh: Triangle) => r => sh.base * sh.height / 2 )
The compiler checks exhaustiveness. If you add d Pentagon : Shape
tomorrow, every .on() that dispatches on Shape will fail to compile
until you add the Pentagon handler. This is the guarantee: add a variant,
every handler site is flagged. No runtime surprise.
Compare with other languages:
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rect": return shape.width * shape.height;
// forgot triangle — compiles fine, crashes at runtime
}// forgot Triangle — compile error: // "Triangle not handled in .on() dispatch on Shape" shape.on( (sh: Circle) => r => 3.14159 * sh.radius * sh.radius, (sh: Rect) => r => sh.width * sh.height ) // ✗ error
Why arity, not keywords. The handler's parameter type IS the
pattern. (sh: Circle) => matches when the value is a Circle.
(sh: Rect) => matches when it's a Rect. No case,
no match, no is. The function's arity — the number and types of
its parameters — determines which handler fires. This is the same
mechanism that .on() uses for iteration:
// Iteration: .on() with one handler, one param items.on(item => process(item)) // Dispatch: .on() with multiple handlers, typed params result.on( (val: Success) => log(val.data), (err: Failure) => log(err.message) )
Similarly, ? replaces if and ! (in the signature) replaces
throws. U doesn't have control-flow keywords — it has operators
that compose with the type system:
// ? replaces if: val < 0 ? x NegativeError() // guard: if val < 0, throw val < 0 ? r => 0 // guard: if val < 0, return 0 // The "else" is just the next line: f abs(val: I) -> I val < 0 ? r => 0 - val // if negative, return negated r => val // otherwise, return as-is // ! in the signature replaces throws/try/catch: f parse(input: S) -> Config ! ParseError ! IoError // callers MUST handle both errors or declare them in their own ! // :: replaces instanceof/is: val :: Circle ? log(val.radius) // narrows type in scope
The pattern: where other languages add keywords (enum, switch,
match, if, else, instanceof, throws),
U uses operators (.on(), ?, !, ::) that compose
with the type system. Fewer keywords, more safety — the compiler
checks exhaustiveness, not the programmer's memory.
U has two operators for "this might not work": x for errors and ?? for none.
They're different things and U keeps them separate.
// ?? is for missing values (map lookup, optional field)
name = req.query.get("name") ?? "World" // get() returns S +N
port = config.get_int("port") ?? 8080
// x is for operations that THROW on failure
data = file.read("config.json") x "{}" // file.read throws FileError
// x chains right-to-left: try each, use first that succeeds
config = file.read("a.json") x file.read("b.json") x "{}"
// x.on() dispatches by error type — terminal (no x after it)
user = fetch(id) x.on(
(err: Timeout) => cached_user,
(err: NotFound) => guest_user()
) // if AuthError thrown, it propagates up?? handles none — something was simply absent.
x handles errors — something tried and failed.
x chains right-to-left. x.on() is terminal: if its handlers
throw, the error propagates to the caller.
Without +R, values live on the stack. +R moves them to the refcounted heap — the compiler emits retain/release. No garbage collector, no manual free.
d Session
token: S
user: S
f open(tok: S, user: S) -> Session +R
r => Session({ token: tok, user: user })Garbage collectors exist because reference cycles keep objects alive forever.
U eliminates cycles at compile time: the compiler builds the type reference graph,
detects potential cycles, and requires +R(parent) on back-edges.
Strong +R references must form a DAG.
d Element children: [Element +R] // strong — parent owns children parent: Element +R +N // ✗ compile error! // "Element.parent is a back-pointer to Element. // Mark it +R(parent) to prevent cycles."
d Element children: [Element +R] // strong — parent owns children parent: Element +R(parent) +N // weak — doesn't hold parent alive tag: S
+R(parent) is a weak reference — it doesn't increment the refcount.
When the parent is freed, the weak ref becomes none. This is why
+R(parent) implies +N (nullable).
The compiler checks cross-type cycles too:
d Author books: [Book +R] // strong — author owns books d Book author: Author +R // ✗ error: Author → Book → Author cycle! // Fix: author: Author +R(parent) +N
The full set of +R qualifiers:
+R strong, downstream — slab-allocated, ARC +R(parent) weak, upstream — no retain, implies +N +R(pool) region/arena — bump-allocated, freed with pool +R(GPU) GPU device memory
Why this eliminates GC. When the root reference dies, its children's
refcount drops, their children's refcount drops — the tree is freed deterministically.
+R(parent) weak refs can't prevent deallocation. The DAG guarantee means
every object has a clear owner. This is the same principle that makes the DOM's
ownerDocument work — U just enforces it at the type level.
The core safety rule: +M defaults to things owned by an instance. Everything else is -M. Parameters are -M — the callee borrows read-only. -M propagates inward through fields and elements.
// -M parameter: can't modify the Point through it
f describe(pt: Point) -> S
// pt.x = 5.0 ← compile error: -M propagates
r => "(" + pt.x.__string__() + ", " + pt.y.__string__() + ")"
// +M parameter: declares intent to modify
f zero_out(pt: Point +M)
pt.x = 0.0
pt.y = 0.0
// Element +M hole-punch: container frozen, elements writable
f normalize(pts: [Point +M])
// pts.push(...) ← compile error: container is -M
[1..pts.len].on(idx => (
pts[idx].x = pts[idx].x / 100.0 // ok — element is +M
pts[idx].y = pts[idx].y / 100.0
none
))x works in three positions. Prefix: throw. Postfix: capture or provide fallback.
Statement: register reusable policies. No try/catch. No catch(Throwable). No silent swallowing.
d InsufficientFunds
required: Q -M
available: Q -M
f withdraw(account: Account +R, amount: Q) -> Q ! InsufficientFunds
account.balance < amount ? x InsufficientFunds({
required: amount, available: account.balance
})
account << { balance: account.balance - amount }
r => account.balanceThe ! in the signature is the contract. Callers must handle it.
// 1. Fallback value — catches any error, provides substitute balance = withdraw(acct, amount) x 0 // on error: balance = 0 // 2. Typed dispatch — per-error fallback values balance = withdraw(acct, amount) x.on( (err: InsufficientFunds) => err.available // return what's left ) // No "capture and check later" — handle it HERE or propagate it
Reusable policies go at the top of a function. They're middleware: retry,
log, observe. They return L +N (true=handled, none=propagate) and are generic —
define once, use across many functions.
f process(input: S) -> Dashboard ! AuthError
x.on(retry_policy) // retry timeouts (reusable)
x.on(logging_policy) // log all errors (reusable)
// Happy path — clean
user = fetch_user(input) x guest_user() // typed fallback
data = db.get(user.id) x default_data() // typed fallback
r => Dashboard({ user: user, data: data })Policies fire first (retry, log). If the error still reaches the expression,
postfix x provides the fallback value. Two layers that compose.
+G fields belong to the class, not the instance. They are -M by default — global constants.
Mutable globals need +G +M and all writes must use <<.
d Config
MAX_RETRIES: I +G = 3 // global constant — no annotation needed
APP_NAME: S +G = "MyApp" // global constant
d Counter
val: I +G +M = 0 // global mutable — MVCC-managed
f+G increment() // +G = static method (no t)
Counter << { val: Counter.val + 1 }
// Counter.val = 5 ← compile error: use << for +G +M<< is the atomic write — patches one or more fields in a single operation. No lock, no mutex, automatic retry on conflict.
d Metrics
requests: I +G +M = 0
errors: I +G +M = 0
last_path: S +G +M = ""
f on_request(path: S)
// Patch two of three fields atomically
Metrics << { requests: Metrics.requests + 1, last_path: path }
f on_error(path: S)
Metrics << { errors: Metrics.errors + 1, last_path: path }
// Instance MVCC — t can use << too
d Account +R
balance: Q
holder: S
status: S
f deposit(amount: Q)
// Patch balance and status, leave holder unchanged
t << { balance: t.balance + amount, status: "active" }Multiple << patches, one atomic commit. Only pure, deterministic code inside.
Retries automatically on conflict. x Rollback(...) exits cleanly.
d AccA
bal: Q +G +M = 0.0
name: S +G +M = ""
last_tx: I +G +M = 0
d AccB
bal: Q +G +M = 0.0
name: S +G +M = ""
last_tx: I +G +M = 0
f transfer(amount: Q, tx_id: I) ! InsufficientFunds
rate = get_rate() // +D — before the block
<< (
fee = compute_fee(AccA.bal, rate) // -E -D only ✓
AccA.bal < amount + fee ? x Rollback("insufficient")
AccA << { bal: AccA.bal - amount - fee, last_tx: tx_id }
AccB << { bal: AccB.bal + amount, last_tx: tx_id }
// name is NOT patched — stays unchanged
) // all-or-nothing
log("transferred") // +E — after the blocka before a call makes it async — a fiber. The scheduler handles concurrency
on one thread with epoll. No ThreadPoolExecutor, no GIL.
a f fetch(url: S) -> S resp = a HTTP.get(url) r => resp.text() // Fan-out: fetch all URLs concurrently f fetch_all(urls: [S]) -> [S] tasks = urls.map(url => a fetch(url)) r => tasks
e(obj) wraps an object as an event emitter. .on() subscribes.
owner defaults to t inside methods — cleanup is automatic.
d ClickEvent
x: I -M
y: I -M
d Button +R
label: S
f click(x: I, y: I)
e(t).emit(ClickEvent({ x: x, y: y }))
d Page +R(RAII)
f setup(btn: Button +R)
// owner defaults to t — auto-removed on Page cleanup
e(btn).on(click => Log.info("clicked"))
// need the source? second param is EventContext
e(btn).on((click, ctx) => (
Log.info(ctx.target.label + " at " + click.x.__string__())
))The handler is a pure function: Request in, Response out.
The runtime handles TCP, epoll, fibers, keep-alive.
f list_users(req: Request) -> Response
users = Database.Query({ store: "users" })
.select(["name", "email"])
.orderBy("name", "ASC")
.limit(50)
.fetchAll()
r => Response.json(users)
f main()
serve(8080, {
"GET /users": list_users,
"POST /users": create_user
})The query builder produces parameterized SQL — dialect-aware ($1 for Postgres, ? for MySQL). No injection, ever.
d User : Database.Row
name: S
email: S
score: I
f top_users(min_score: I) -> [User]
r => Database.Query({ store: "users" })
.where("score", ">=", min_score)
.orderBy("score", "DESC")
.limit(10)
.fetchAll()
// Transactions work with database rows too
f award(from_id: I, to_id: I, points: I) ! NotEnough
<< (
sender = Database.Query({ store: "users" }).where("id", "=", from_id).fetchRow()
recipient = Database.Query({ store: "users" }).where("id", "=", to_id).fetchRow()
sender.score < points ? x Rollback("not enough")
sender << { score: sender.score - points }
recipient << { score: recipient.score + points }
)Template literals with compile-time validation. SQL templates lower to prepared statements automatically.
// HTML template — injection-safe
f page(title: S, body: S) -> S
r => HTML`<html><head><title>{{title}}</title></head>
<body>{{body}}</body></html>`
// SQL template — compiles to prepared statement
f find_user(email: S) -> Tree
r => SQL`SELECT * FROM users WHERE email = {{email}}`Two orthogonal axes that track what a function does:
+E = has side effects (I/O, logging, network).
+D = nondeterministic (random, clock, external input).
The defaults are -E -D — pure and deterministic. This is what makes << ( ) transactions safe: the compiler only allows -E -D code inside.
// Pure, deterministic — safe for transactions, memoization, vectorization
f compute_fee(balance: Q, rate: Q) -> Q
r => balance * rate * 0.01
// Effectful — writes to the outside world
f+E save_log(msg: S)
File.append("/var/log/app.log", msg)
// Nondeterministic — reads from unpredictable source
f+D roll_dice() -> I
r => Random.int(1, 6)
// Both — effectful AND nondeterministic
f+E+D record_timestamp()
ts = Time.now() // +D: reads clock
File.append("log.txt", ts.__string__()) // +E: writes file
// This is why transactions work:
<< (
fee = compute_fee(bal, rate) // -E -D ✓ — allowed
// save_log("hi") // +E — compile error!
// dice = roll_dice() // +D — compile error!
Account << { balance: bal - fee }
)e value yields a value from a generator — the function suspends and resumes when the consumer pulls the next value. +W marks the return type as an infinite stream. [1..w] is a built-in infinite range.
// Fibonacci generator — infinite stream f fib() -> I +W prev = 1 curr = 1 [1..w].on(idx => ( e curr // yield current value next = prev + curr prev = curr curr = next none )) // Consume: take first 10 Fibonacci numbers f first_ten() -> [I] r => fib().take(10) // Pipelines work on infinite streams f even_fibs() -> [I] r => fib().filter(num => num % 2 == 0).take(5)
z f declares a function that runs at compile time. The compiler evaluates it, inlines the result, and emits no runtime call. Must be -E -D (pure, deterministic) — the compiler can't do I/O.
// Computed at compile time — no runtime cost
z f factorial(num: I) -> I
num <= 1 ? r => 1
r => num * factorial(num - 1)
// The compiler evaluates this and emits: result = 120
f main() -> I
result = factorial(5)
r => result
// z f __load__() runs at module load time
z f __load__()
Config << { root: load_config_tree() }
r => nonec expr produces a deep copy. The compiler doesn't copy implicitly — when you want a snapshot of mutable data, you say so.
d State +R data: [I] f snapshot(state: State +R) -> State +R r => c state // deep copy — independent of original // c+R promotes a stack value to the heap f make_heap(val: I) -> [I] +R local_list = [val, val * 2, val * 3] r => c+R local_list // copy to heap, return +R
u f marks a function whose body is generated by an LLM. The compiler
feeds the LLM the signature, modifiers, comments, and call sites across the project.
The generated code is type-checked and modifier-checked like any other code.
/// Rank products by relevance to the query.
/// Prefer exact title matches. Boost recent listings.
u f search(query: S, catalog: [Product]) -> [Product]
/// Detect fraudulent transactions from user history.
/// False positive rate must stay under 1%.
u f fraud_check(txn: Transaction, history: [Transaction]) -> RiskScore
// No comment needed — the signature says everything:
f total(prices: [Q]) -> Q
r => prices.reduce((acc, price) => acc + price, 0)
// The human writes the wiring. The AI writes the logic.
f main()
serve(8080, {
"GET /search": (req) => Response.json(
search(req.query["q"], load_catalog())
)
})Add u — the AI takes over. Remove u — the generated body
inlines into your source and you own it. One letter toggles the human-AI boundary.
When an LLM generates a U file from scratch, every function is u f by
default — honest about its provenance, ready for a human to claim.
+V on data means vector-ready layout. On a function, it means vectorizable computation.
The emitted C uses SIMD intrinsics — SSE/AVX on x86, NEON on ARM.
f scale_all(arr: [I +V] +R) -> [I +V] +R r => arr.map(val => val * 3) f dot_product(values: [N], weights: [N]) -> N sum: N +M = 0.0 [1..values.len].on(idx => ( sum = sum + values[idx] * weights[idx] none )) r => sum // -M default on sum forces local accumulator // → GCC/Clang auto-vectorizes into SIMD
+R(GPU) places data in device memory. A .map() over it becomes a WGSL compute shader.
f brighten(pixels: [I] +R(GPU)) -> [I] +R(GPU) r => pixels.map(px => px * 3 + 1)
The specification is the complete reference. The interview page compares U to Python on five real problems. The snippets page has copy-paste examples. The feature status tells you honestly which of it runs today.