Syntax tour (the language on one page)
The “part 2”: a walk through the whole syntax in commented code, grouped by theme, as a quick reference. Nothing is new, it is the consolidation of sections 1 to 15, written as you would write it. Each block is an executable example; the comments point to the decision behind it.
Declaration and mutability
Section titled “Declaration and mutability”x := 5 // declares immutable, runtime (default): shortcut for 'let'let y := 5 // immutable, explicit form (identical to 'x := 5')var z := 5 // mutableconst N := 64 // compile-time constant (section 10)name: string = "ana" // explicit type Odin-style; ':=' is ': =' with the type omitted
z = z + 1 // a bare '=' only assigns, and only to a 'var'; the ':' is what declares// var w := compute(); return w // ERROR: 'w' is var but was never mutated. Use the immutable form
// A single pointer type; 'mut' is write-back. Both require a 'var' target.p := &z // &place = address of a placetotal := *p // *p = dereffn bump(v: mut int) { v = v + 1 } // write-back parameterbump(mut z) // mutates z in-place; bump(mut x) would be an ERROR (x is immutable)Control flow: unified loop and match
Section titled “Control flow: unified loop and match”loop { break } // no header → infiniteloop active { ... } // boolean expression → whileloop item in list { ... } // IDENT in iterable → for-eachloop i in 0..8 { ... } // exclusive range (..= is inclusive)loop i := 0; i < 8; i = i+1 { ... } // C-style; ';' delimits the 3 parts (parentheses optional)
// match WITH a subject → matches patterns (the "switch")match parse_port(input) { Ok(p) => listen(p) Err(error.Empty) => use_default() Err(error.NotNumber) => abort() Err(error.OutOfRange) => abort() // closed domain: all the variants, '_' would be an error}
// match WITHOUT a subject → waits for several channels, reacts to the first ready (the "select")match { msg := -> chan_a => handle_a(msg) msg := -> chan_b => handle_b(msg)} timeout(1s) { give_up()}
defer file.close() // deterministic cleanup; runs on scope exit, LIFOFunctions, UFCS and dispatch by type
Section titled “Functions, UFCS and dispatch by type”// Modifiers: pub? comptime? unsafe? fn ('@generator' is a decorator: above or inline)pub fn add(a: int, b: int) -> int { return a + b }
// UFCS: x.f(y) is sugar for f(x, y): pipeline with '.', and lambdas are 'fn(x) => expr'errors := lines .filter(fn(l) => l.contains("ERROR")) .map(fn(l) => parse_entry(l))
// Generic with uniform logic:fn show[T + Display](x: T) -> string { return x.to_string() }
// Dispatch by type, small variations together → internal 'match T' (closed domain, no '_'):fn encode[T: {int, bool}](x: T) -> []byte { match T { int => return x.to_bytes() bool => return x.to_bytes() }}
// Large or separate variations → external set (router by type, resolved at compile time):fn to_string = { int_to_string, bool_to_string }Types: struct, enum, union, interface
Section titled “Types: struct, enum, union, interface”decl User { id: int // private to the module by default (encapsulated) name: string pub email: string // field exposed individually to other modules}
// enum WITHOUT a payload = simple enumeration / state machine (no iota; each variant is a value):decl State { Idle; Running; Done }
// enum WITH a payload = sum type (the payload is optional per variant, and can mix):decl Event { Tick; Click(int, int); Quit }
// exhaustive match over enum: covers all the variants and '_' is a compile error// (adding a variant breaks the build until you handle it, the '_' would nullify that):fn step(s: State) -> State { match s { Idle => return Running Running => return Done Done => return Done }}
// match with a payload binds the fields by position; closed domain → all the variants (no '_'):fn describe(e: Event) -> string { match e { Tick => return "tick" Click(x, y) => return "click at " + x.to_string() Quit => return "quit" }}
// Method = external function with a receiver (Go style); the struct only holds data:fn (u: User) display() -> string { return u.name + " <" + u.email + ">" }
// Interface = set of methods; implementing the methods ALREADY satisfies, without "implements":decl Writeable { fn write(data: []byte) -> error}
// union = overlapped bytes, always unsafe (reinterpretation):unsafe union Bits { f: f32; raw: u32 }
flag: Optional[int] // explicit absence; integers i8..usize, byte = u8, IEEE floatsStrings
Section titled “Strings”s := "Hello {{name}}!" // UTF-8 string; named interpolation (via Display)n := s.char_at(0) // one grapheme; there is NO pure 's[i]' (ambiguous → error)view := s.chars[2..6] // VIEW by graphemes ('..'), keeps s alive; .bytes/.codepoints samecp := s.chars_copy(2, 6) // sibling method; ':' / '_copy' = O(n) copyraw := s.to[u16]() // boundary conversion, explicit → []byteeq := a == b // '==' is byte equality; '.equals(_, .semantic)' for Unicodevar b := "x"; b.append("y") // mutable only with 'var'; growth by doublingErrors: Result, catch, match |e|
Section titled “Errors: Result, catch, match |e|”fn parse_port(s: string) -> Result[u16, error{Empty, NotNumber, OutOfRange}] { if s == "" { return Err(error.Empty) } // ...}
// 'catch |e|' handles the error as a unit and ESCAPES (return/panic): propagates or returns a fallback:port := parse_port(input) catch |e| { return default_port() }
// 'match |e|' branches by variant (trailer; '|e|' is the error), and like catch each arm escapes.// Single variant → use catch. Recovering WITH a value (giving cfg a default) is match over the Result.cfg := load() match |e| { error.NotFound => return e // propagates (sugar → Err(e)) error.Corrupted => runtime.panic("unreadable config") // aborts}
// Three forms, no overlap: only-values → tuple (string, string); only-error → error-set alone;// value(s) + error → Result. There is no positional return with an error (an error entered, it is Result):fn split_pair(s: string) -> (string, string) { ... }Concurrency and channels
Section titled “Concurrency and channels”// Channel typed by the perspective of whoever holds the handle: <-T = "I send T", ->T = "I receive T"c: Channel[<-int, ->string]spawn worker(c) // the compiler verifies that the sides are complementary
fn worker(c: Channel[<-string, ->int]) { val := -> c // receives (blocks until it arrives) c <- val.to_string() // sends (async, fire-and-forget)}
reply := server <-> request timeout(5s) // sync request-reply; timeout MANDATORY (without it, error)
spawn task(data) catch |e| { log(e) } // process with a failure handlerspawn { // group of processes (supervision emerges from the structure) indexer(docs) catch |e| { ... } notifier(subs) catch |e| { ... }}Memory: @mm, @transfer, @promote
Section titled “Memory: @mm, @transfer, @promote”fn build_report() -> Report { @mm(arena) // allocation strategy of this scope: arena | gc | none buf: HugeBuffer @mm(arena) = alloc_buffer() // (postfix form fixes by-value) defer free_arena() // @mm(none)/manual frees with defer // ...}
@mm(arena) spawn producer(data @transfer) // @transfer invalidates 'data' here; arrives at the destination with its mm@mm(gc) spawn consumer(chan) // Arena → GC via @transfer is valid
promoted := node @promote(gc, deep) // copies to another MM (shallow is the default; 'deep' explicit)Comptime and generics
Section titled “Comptime and generics”// 'type' is a first-class value; a generic is a function that receives/returns 'type':decl List[T] { items: *T, len: usize } // sugar for the comptime fn belowcomptime fn List(T: type) -> type { return decl { items: *T; len: usize }}
const nums := List[int] // compile-time call → concrete type (monomorphized)buffer: [N]u8 // '[...]' accepts comptime values, not just types
comptime { // block that runs in the compiler assert (N <= 1024)}
const backend := use(if target.arch == x86 { return "backend_x86" } else { return "backend_wasm" })Generators
Section titled “Generators”@generator fn fibonacci() -> int { var a := 0 var b := 1 loop { yield a next := a + b a = b b = next }}
loop n in fibonacci().take(10) { io.print(n) } // consumed like any iterableDesign by contract and unsafe
Section titled “Design by contract and unsafe”// Boundary contract: pre-condition stacks; post-condition has two equivalent forms.@requires(i < buf.len)@requires(buf.len > 0)@ensures(u8 == buf.bytes[i]) // directive form (stacks, references the return by type)fn at(buf: Buffer, i: usize) -> u8 { // or terse, in the return type: '-> u8 == buf.bytes[i]' // the contracted pre-condition makes 'at' SAFE to call (the compiler checks at the call) return unsafe raw_read(buf.ptr + i) assume "valid ptr: Buffer invariant + bounds from @requires"}
b := at(buffer, 3) // safe call: the compiler checks 3 < buf.len
// named return: mandatory ONLY when a return type repeats and there is @ensures over it@ensures(lo > 0)@ensures(hi > 50)fn split(...) -> (lo: u8, hi: u8) { return (a, b) } // names = slot labels; return free
// Killing an operation in the body: strong (checks, panic) vs weak (case-B, non-empty reason, audited)y := unsafe x.raw assume "intentional reinterpretation float→bits"result := unsafe { // block = expression-scope v := raw_read(p) assert (p_valid) // later assert sees what came before ('p_valid') return v * 2 // value goes out by return (expression → goes to 'result')} assert (p_valid) // shared trailer = entry pre-conditionFFI with C
Section titled “FFI with C”const c := @cimport("stdio.h") // C enters as a module (comptime) → c.stdio@cimport("gcc") // fundamental types → c.gcc.int, c.gcc.size_t (the compiler's ABI)
buf: *c.gcc.char = c.stdio.malloc(n) // all qualified under 'c.'; runtime type + header fnunsafe c.stdio.printf("%d", count) // calling C is unsafe; the variadic is case-B (types on your account)
@repr(c) decl Point { x: c.gcc.int } // C-faithful layout at the boundary (internal structs are optimized)@pin handle // a @mm(gc) object crossing to C requires @pin (otherwise it does not compile)mine := data @promote(gc) // C memory (@mm(c)) repatriated to my GC
@callback fn cmp(a: *c.gcc.void, b: *c.gcc.void) -> c.gcc.int { ... } // fn callable by CInline assembly
Section titled “Inline assembly”@asm(x86, intel) // assembly-function: body is asm, returns a valueunsafe fn add10(v: u32 @asm(in, rax)) -> u32 @asm(out, rbx) { mov rbx, rax add rbx, 10}
var lo: u32; var hi: u32 // inline block: runs asm in the middle of normal code@asm(x86, intel)@asm(out, rax) lo@asm(out, rdx) hi@asm(clobber, "memory") // EXPLICIT clobbers in the block (the function uses the ABI)unsafe { rdtsc }
@asm(wasm, wasm) // WASM has no registers: binds by param/localunsafe fn add(a: u32, b: u32) -> u32 { local.get a local.get b i32.add }Observability
Section titled “Observability”procs := runtime.processes() // cheap snapshot: processes, memory, tree, @mm per processp := runtime.process("db_writer") // by registered NAME (restart-stable); runtime.group("h") for groupsme := runtime.self() // its own reference, from inside any process
@register("db_writer") // names the process (string, stable); @register("h", group) for groupsspawn db_writer(conn) // a loose 'spawn' registers nothing (fire-and-forget, zero cost)
ev := runtime.trace(p, [.send, .receive, .channel_block], sink: .channel, sample: 100)loop e in ev { match e { ... } } // capture = ring buffer (cheap); consumption via channel/callback/endpointUI extension (.mkoui)
Section titled “UI extension (.mkoui)”@componentfn Counter(start: int) { @state n := start // reactive state (decorator, not keyword) @derived label := "clicks: {{n}}" // recomputes when 'n' changes fn inc() { n = n + 1 } // named handler return { // markup in the return block (no implicit return) <button class="btn" onclick={inc}>{{label}}</button> // handler: { } ; text: {{ }} <style> .btn { padding: 8px 16px; } // scoped CSS: '.btn' → '.btn_a3f8' (hashing); typo = error </style> }}
@jsimport("dom", browser) // FFI of JS: all under 'js.'; the environment propagates @server/@client@css("tailwind.css") // external CSS: global, names preserved, enters the check
@server fn load(id: int) -> Post { return db.get(id) } // @server/@client = restriction; the rest crosses// islands: only the interactive parts become WASM; the rest is static HTML// server/client boundary = network channel (net.connect[T], T + Serializable)// where WASM does not run: transpile of the .mkoui to JS via build --target=js (total parity)Modules and visibility
Section titled “Modules and visibility”// a file inside a project with mk.project at the rootuse myapp/store // local: prefix = package name (absolute, never '../')use json // stdlib: bare (comes with the compiler)use github.com/acme/http as web // external: URL form + aliasuse json.{parse, decode} // brings specific names into scope
priv fn helper() { ... } // visible only in this FILEfn internal() { ... } // visible in the MODULE (default)pub fn api() { ... } // visible to other MODULES
module quickscript // (alternative) transportable single-file module, embedded checksums