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)
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 })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 ErrorType(...) throws a typed error. ! in the return type declares what can fail.
x.on(handler) registers a catch handler — errors dispatch by type, not by position.
d DivideByZero : Error f safe_div(num: N, den: N) -> N ! DivideByZero den == 0.0 ? x DivideByZero() r => num / den f try_it() -> S x.on((err: DivideByZero) => ( r => "cannot divide by zero" )) result = safe_div(10.0, 0.0) r => result.__string__()
+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
+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.