Start here — from nothing to your first debugged program, then onward.
One clone is the whole setup.
mica-lang/mica-container is the
starter for Mica: the dev container, the whole example suite, the benchmarks, F5
debugging across every one of them, and the working files this series is
written against.
git clone https://gitlab.com/mica-lang/mica-container.git
code mica-container # then: Reopen in ContainerNothing is installed on your machine but Docker and VS Code. If you would rather not clone anything, part 1 below builds the same setup from four files you paste yourself.
Two courses
Program in Mica
The tutorial series
A guided path from a first program through the type system, the memory model and the concurrency model — written against the shipping compiler, with every example compiled and every command run before it is published. Each tutorial uses one example, and the examples live in the tutorial repository.
The path grows a section at a time. Within a section the order is the reading order; between sections, a tutorial says at the top what it assumes.
Getting started
| Part | Tutorial | You end up with |
|---|---|---|
| 1 | Get started with Mica | a dev container, your first program, and a debugger session — breakpoints, variables, stepping |
| 2 | The shape of a Mica program | the four parts every program has, why nothing is ambient, and a format string checked before the build |
| 3 | Types that change, and what it costs | when a value widens on its own, when you must write as, and what a narrowing cast does to a number that will not fit |
| 4 | Choices and repetition | the two ways a program decides and the three ways it repeats — each loop stating what it knows before it runs, on one walk whose output checks itself |
| 5 | Procedures and parameters | the same walk with its work named — arguments as snapshots, results as values, and the one write into your variable spelled address where you can see it |
| 6 | The first failure | a function that admits it can fail in its signature, a call the compiler makes you finish, and a missing file that arrives as a name, not a number |
| 7 | The crossroads | one program using the whole numbered path plus its first record — and the guided map into the themed tutorials |
Alongside them: installing Mica, the project file, Mica as a scripting language, and the two small features every program leans on — initializers, and the leave family.
Values and data
| Tutorial | You end up with |
|---|---|
| Ordinals: values that have a position | why one property explains for, case, array indexing and sets at once — arrays indexed by an enumeration rather than by an integer, and the two boundaries (wide integers, error codes) worth knowing |
| Values, and arrows drawn by hand | what an assignment copies — everything — and the one construct that shares: a pointer created with address, dereferenced with value, visible at every call site that can change your variable |
| Records, arrays, and bounds you choose | shapes named once in type, matrices as one type with two ranges, copies that reach all the way down — and an index checked at compile time or trapped at its named line |
| Dynamic arrays: growth under the value rule | Append and Length, jagged tables spelled as what they are, and an assignment that still copies — so no callee can grow your list behind your back |
| Order and disorder | Sort as a native generic heapsort — in place, no allocation, worst case equals best — SubArray as the named piece, and a seeded Shuffle that is bit-stable on every machine |
| Conformant arrays: bounds as parameters | one routine for fixed arrays of any bounds, a constant view that provably cannot write, and a pointer view consented to with address at the call |
| The sequence families: four faces, one law | fixed array, vector, dynamic array and span side by side — one reader vocabulary over all four, a copy that owns its elements all the way down, a borrow spelled where it is taken, the element verbs, and a whole value written as one list |
| Sets: membership as a value | set of a domain you named, the + - * algebra, the recovery-set idiom every recursive-descent parser needs — and bitset[N] when the positions are raw |
| Maps: two reading moods, one value rule | a typed container where Get traps on the key you promised was there, Has/GetOr ask first, snapshots walk in insertion order — and assignment deep-copies, no hidden sharing |
| Bit records: bits with names | typed fields packed into exactly N bits, at pins the compiler proves against the derived layout, and the masked case whose arms read like the datasheet’s command table |
Math and shapes
| Tutorial | You end up with |
|---|---|
| Vectors and matrices | shape carried in the type — products refused when dimensions cannot meet, elementwise arithmetic with the scalar meet, * as the linear-algebra product with Dot and Transpose named beside it, and copies that stay copies |
| Dynamic tensors | vectors and matrices whose extents live in the value: Resize and the shape readers, the same operators at run-time extents under an always-on shape guard, static and dynamic mixing under one dimension-wise law, broadcasting spelled with RowSpread and ColumnSpread, Sum along a constant axis, and the rank-general tensor word above the pair |
| Linear algebra | Solve, Invert, Cholesky, and Qr over dynamic matrices — singularity as an expected answer on the failure channel, the value model as the working storage, and every verification written in the operators themselves |
| Quantized tensors | the one-byte element families int8 and uint8: storage the inference world packs its weights into, refused operators that name the road instead, and WideningProduct — the integer matmul whose int32 accumulation stands in its own name |
| Tracked values and the tape | the autograd surface as a language feature: the tracked pair, the recording window in the program’s own body, one backward walk answering every blame — with the recorded vocabulary, fan-out accumulation, the two honest zeros, and every wall quoted from the shipped binary |
| Model depth | a model as records of tracked places and the optimizer walk as your own visible loop: the depth loop recording through blocks[k].w, Gradient per place, an Adam step over plain twin records through the conformant borrow, and the retrain window proving the cycle |
| The learning toolkit | the everyday pieces of a training loop: Relu/Gelu/Silu and RmsNorm recorded with their rules, the visible dropout pair whose gradient is the printed mask, MseLoss, the update family SgdStep/AdamWStep/ClipGradNorm/CosineSchedule, the elementwise HadamardMap (the SwiGLU gate’s own verb), and the seeded XavierInit/HeInit |
| Batched training | pytorch’s training shape whole: a batched two-block transformer with the Gather embedding, Reshape on both sides of the causal batched attention, seeded dropout, the batched CrossEntropy over a matrix of targets, and the visible AdamW walk under the cosine schedule — every digit oracle-checked |
| Image networks | the convolutional family: Conv2d over channels-first tensors, the pooling pair with the documented tie, BatchNorm whose running statistics are your own visible values with ChannelScale as the inference line, and a small classifier trained end to end — every digit oracle-checked |
| The GPU, without kernels | a complete GPU program with no kernel in it: a tensor declared on gpu, the two transfer verbs, the mixing refusal quoted — and the same mathematics printing identical digits on host and device |
| The math unit | generic scalar verbs at full type fidelity, constants that are declarations, IEEE classification through pure arithmetic, and the libm seam one import block tells whole |
| Big integers | int128 to uint256 over a parameterized wide family, and the unbounded bigint with its verb arithmetic — 25 factorial printed whole, and the checked tier trapping where int64 would lie |
AI — from the first guess to your own GPT
The course has its own page: Learn AI with Mica, with the map of a GPT part by part.
A course for readers with no machine-learning background, ending with a GPT you train yourself. It keeps one promise the whole way: every number you print is the same every run — learning you can repeat digit for digit.
| Tutorial | You end up with |
|---|---|
| The guessing machine | what learning is, before any mathematics — a machine that fits a rule to observations with nothing but guess, measure, nudge, and a loss that falls to zero |
| Downhill in the fog | the gradient: direction and stride read off the guess itself, the measured and the computed slope agreeing to the last digit, and the descent that replaces search with arithmetic |
| The notebook that runs backward | the tape: ’tracked’ knobs, the recording window, and one ‘Backward’ walk answering every slope — chapter 2’s descent reproduced digit for digit with the algebra done by the machine |
| Many knobs at once | vectors and the dot product: the same loop when the guess is a vector and a bias, one backward walk answering a whole gradient vector, and the shape checked before the run |
| The bent wire | the wall no weighted sum can climb — XOR’s loss flattening at exactly one — and ‘Tanh’, the one bend that climbs it with the same descent |
| Layers | what a hidden layer learns: the four corners’ hidden coordinates printed before and after training, the want-1 corners landing together, and the one weighted sum that reads the learned space |
| Blame flows backward | backpropagation by hand, exactly once: three per-link rules, a ledger walked from the loss to the knobs, and the tape’s answers matching yours to the last digit |
| The art of the step | one unfair valley descended three ways: the wall pricing the plain step, momentum saving it, and Adam — with the GPT’s own β₁ and β₂ — repealing it |
| Text becomes numbers | tokens, one-hot rows, and the embedding as learned meaning — closed by the smallest language model there is, trained to exactly 1.000000 on its three transitions, with the unseen character learning exactly nothing |
| Looking at everything at once | one attention head at the smallest honest size: query, key, and value in five recorded lines, the causal mask’s exact zero, the soft split’s exact halves, and blame walked backward through the softmax — exactly |
| The GPT, assembled | Gpt.mica read as one page: the eighteen-sentence window with each line’s home chapter, the 786944-parameter arithmetic, nine tensors, and the few honest newcomers — scale, residuals, layer norm, cross-entropy |
| Train your own GPT | a corpus, one command, minutes of training, and text in your corpus’s style — reproducible to the digit, run after run, with the device twin’s diff showing exactly what moving training to a GPU means |
| Deterministic reductions | Sum, Mean, the extremes and their indices on one documented pairwise tree — the same tree serially and in parallel, on both architectures, so a float reduction is a promise of the language |
| WYSIWYG slicing | every slice a named copy — Row, Column, SubMatrix, Window — shaped values built off the target’s declaration, with no view-or-copy roulette |
| Spans and the lending law | span of T as the borrowed view in var, parameter and return positions — RowView, WindowView, and the lending law keeping every borrow inside a lifetime the caller can see |
Strings
| Tutorial | You end up with |
|---|---|
| Text: the value, the builder, and the window | the immutable string value that counts runes, the unit whose verbs answer new strings, the builder for the loops where + would copy — and the borrowed window that tokenizes with zero allocation |
| UTF sources: any script, one meaning | identifiers in any script with nothing special about them, rune counts no encoding can change, and the build-time encoding choice whose entire porting cost is one letter per format specifier |
| Pattern matching | patterns compiled once into a linear-time engine no input can make pathological, a closed escape list refused loudly at compile — and rewriting, counting and groups over runes |
Files
| Tutorial | You end up with |
|---|---|
| Files: the typed file and the failure channel | text out and back through one builder, a missing file as Enoent by name, byte positions you can ask for — and the two error kingdoms drawn through one surface |
| Typed transfers: whole values, and a file as an array | WriteValue/ReadInto sized by the type itself, the plain constraint that refuses to write a heap descriptor’s bytes, and Seek arithmetic that indexes a file of records like an array |
| Paths and directories | the paths unit as pure text algebra — total, failure-free, industry semantics verb by verb — and the directory walk where a path meets the filesystem and its failure channel |
Memory
| Tutorial | You end up with |
|---|---|
| Memory classes: hosted, and a fixed arena | the same source under the OS heap and under one fixed block — five thousand activations through a 16 KiB arena, and a budget that fails loudly at its named line |
| Heap, end to end | an allocation as an obligation with exactly four discharges, use-after-free and double-free as compile-time reports, and the checked tier guarding what no proof can reach |
| Ownership and borrowing | one owner per cell, borrowed as the checked word for everyone else, and the back edge that gives owned structures parents without a cycle of owners |
| What the compiler proves about your heap | the fourteen-row table: every classic heap bug and its exact fate — a refusal or a named-line trap — each produced by a real build or run |
| Defer, end to end | cleanup registered beside the resource, run newest-first at the exit with exit-time values — and the exact rules for early leaves and loops |
Errors
| Tutorial | You end up with |
|---|---|
| The failure channel: errors as values | a function that admits it can fail in its signature, four clauses at the call site checked at compile time — and cleanup plus a propagation trace riding the failure exit for free |
| Error domains: the type behind the failure | a domain that loops and indexes like any ordinal, walls that keep each layer’s failure story its own, conversion as a translation table the compiler completes — and -1-and-errno lifted into codes at the C boundary |
Programs in the large
| Tutorial | You end up with |
|---|---|
| Generics: one algorithm, many types | the gen clause with thirteen capability words, constraints that check the body and the call both ways, and monomorphization proved by an int64 that would not fit an int32 |
| Units and libraries: surfaces, by declaration | exp as the API decision, one import rule with no special cases, and a library whose contract the compiler emits so consumers can never drift from the source |
| Qualified names | imp cstd; making a namespace addressable and cstd.Sqrt saying where a word lives — two import granularities, one lookup mechanism, and a dot that is simply an import in disguise |
| The clock | a monotonic Now whose differences alone carry meaning, duration constants making 250 * Millisecond read as meant, and a Sleep whose floor survives signals |
Concurrency
| Tutorial | You end up with |
|---|---|
| Tasks and the task tree | concurrency where the block is the lifetime — a join no exit path can skip, arguments that are snapshots, and cancellation that still runs your cleanup |
| Generators and streams | a body that produces values one at a time and suspends between them, an instance you can pause and resume, and pipelines that compose |
| Carriers, multicore and busy loops | one thread or many as a build flag: what stays exact when you switch, and why a compute loop with no suspension point still gives way |
| Data-race freedom, proved at compile time | the rule behind synchronized, the two holes it closes that most languages leave open, and an honest account of its edges |
| Channel streams: the push half | the same generator body scheduled as a producing task behind a bounded ring — fan-in, concurrent pipelines, select with a timeout, and the one exclusivity rule that keeps it all lock-free |
| When a stream fails | a generator that admits fails in its type, the pull loop’s own consumption clause, cleanup that provably runs before the consumer hears — and the same word crossing a bounded ring |
| A server is a loop | networking where a connection is a value and a wait suspends one task — so the readable spelling of a server is the scalable one, with no callback anywhere |
| A room is a stream | the multicore chat: one reading task per connection, one relay loop, and the fan-in stream where the conversations cross — plus a shutdown that is nobody’s job |
| The worker pool: fan-out over a ring | one producer, one ring, N workers — a stream of parameter on a task as the fan-out shape, every value landing in exactly one worker, and a pull chain that still refuses with the fix spelled out |
| MPMC: the ring underneath | fan-in and fan-out at once — M producers, N workers, one bounded ring, totals that prove exactly-once delivery on one carrier or four, and the partition-don’t-share discipline the compiler enforces |
| The value stream over the wire | typed values crossing a connection as framed transfers, a stream end that crosses with them — clean, or failing with the producer’s reason — and one on fail consuming every way the wire can go wrong |
| The client road | an address named three ways, the half-close that says “that was all” with the answer still owed, frames assembled from whatever pieces arrived — and the deadline that reaches a parked wait |
| The IPC road | the same verbs with a kernel-namespace name instead of a port — no number to pick, no firewall in the way, nothing left behind, and a refusal that answers a health check by name |
The boundary, and the debugger
| Tutorial | You end up with |
|---|---|
| Mica calls C: a contract, not a header | a C library declared once in a schema-published JSON contract, lifted into Mica with full type checking at every call — scalars, packed aggregates and strings crossing both ways with no adapter |
| C calls Mica: an archive and a naming rule | a plain C program linking a Mica archive like any static library — the target-name linkage rule, the shared ABI story told from both ends, and no runtime to initialize |
| Mica calls Linux: the kernel, without the -1 | POSIX and Linux through contracts embedded in the compiler, every system call an honest fails function — the errno dance compiled, never written |
| Callbacks at the boundary | the third declared subprogram kind: a routine a C library calls back into while your call runs — named only at a contract’s callback slot, never a value, its whole environment the context record you hand over |
| A notes application with sqlite | the road through the sqlite library: open, migrate, insert with binds, pull rows through a generator, read text columns — and the callback road when you want sqlite’s own shape |
| Debugging across the boundary | one gdb session over two languages: the Mica→C→Mica backtrace, both specializations of a generic inspected with their concrete types, the full VS Code setup — every transcript a real captured session |
| Profiling a Mica program | four instruments on one optimized binary: hyperfine for the headline, callgrind for the instruction-exact attribution by procedure and line, gdb stopping inside an inlined function under its own name, perf sampling on a cloud machine — every transcript a real captured session |
Deep dives
Build a compiler with Mica — a complete working compiler, small enough to read in an afternoon, taken apart a piece at a time and rebuilt into native code, chapter by chapter, with Wirth’s 1975 original alongside.
The compiler, exported — one flag writes every intermediate representation to the build directory: fourteen numbered stages from token stream to heap narration, walked over one real program.
The advanced series spans values, strings, files, memory, errors, concurrency and the boundary — every example compiled, run and validated on both architectures before publication.
If you teach, or are working through Mica yourself and find a gap, we would like to hear about it: info@mica-dev.com.