Skip to content
Ridgun
English

Ridgun

The language itself: a compiler that compiles itself, the stable project tool, the specification and the book

index.html645 lines · 123.6 KBRaw
1<!DOCTYPE html>
2<html lang="en" data-theme="dark">
3<head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>The Ridgun Book — Complete Language Guide</title>
7 <style>
8 :root, [data-theme="dark"] {
9 --bg: #0f141c;
10 --bg-sidebar: #141b26;
11 --bg-card: #18212f;
12 --bg-code: #0b0f16;
13 --border: #263246;
14 --text: #e6edf3;
15 --text-muted: #94a3b8;
16 --accent: #38bdf8;
17 --accent-soft: rgba(56, 189, 248, 0.14);
18 --kw: #ff7b72;
19 --ty: #79c0ff;
20 --fn: #d2a8ff;
21 --str: #a5d6ff;
22 --num: #f2cc60;
23 --com: #6e7681;
24 --op: #ffa657;
25 --shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
26 }
27 [data-theme="light"] {
28 --bg: #f8fafc;
29 --bg-sidebar: #f1f5f9;
30 --bg-card: #ffffff;
31 --bg-code: #0f172a;
32 --border: #cbd5e1;
33 --text: #0f172a;
34 --text-muted: #475569;
35 --accent: #0284c7;
36 --accent-soft: rgba(2, 132, 199, 0.12);
37 --kw: #ff7b72;
38 --ty: #79c0ff;
39 --fn: #d2a8ff;
40 --str: #a5d6ff;
41 --num: #f2cc60;
42 --com: #94a3b8;
43 --op: #ffa657;
44 --shadow: 0 4px 16px rgba(15, 23, 42, 0.08);
45 }
46 * { box-sizing: border-box; }
47 body {
48 margin: 0;
49 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
50 background: var(--bg);
51 color: var(--text);
52 line-height: 1.72;
53 display: flex;
54 min-height: 100vh;
55 }
56 /* Sidebar */
57 aside.sidebar {
58 width: 310px;
59 background: var(--bg-sidebar);
60 border-right: 1px solid var(--border);
61 display: flex;
62 flex-direction: column;
63 position: fixed;
64 top: 0;
65 bottom: 0;
66 left: 0;
67 z-index: 20;
68 overflow-y: auto;
69 }
70 .brand {
71 padding: 1.35rem 1.25rem 1rem;
72 border-bottom: 1px solid var(--border);
73 }
74 .brand-top {
75 display: flex;
76 justify-content: space-between;
77 align-items: center;
78 margin-bottom: 0.45rem;
79 }
80 .brand-badge {
81 display: inline-block;
82 font-size: 0.72rem;
83 font-weight: 700;
84 text-transform: uppercase;
85 letter-spacing: 0.08em;
86 color: var(--accent);
87 background: var(--accent-soft);
88 padding: 0.2rem 0.55rem;
89 border-radius: 999px;
90 }
91 .lang-bar {
92 display: flex;
93 gap: 0.25rem;
94 }
95 .lang-link {
96 font-size: 0.72rem;
97 font-weight: 600;
98 text-decoration: none;
99 color: var(--text-muted);
100 padding: 0.16rem 0.42rem;
101 border-radius: 5px;
102 border: 1px solid var(--border);
103 }
104 .lang-link:hover, .lang-link.active {
105 color: var(--accent);
106 border-color: var(--accent);
107 background: var(--accent-soft);
108 }
109 .brand h1 {
110 margin: 0;
111 font-size: 1.25rem;
112 font-weight: 800;
113 letter-spacing: -0.02em;
114 }
115 .brand p {
116 margin: 0.25rem 0 0;
117 font-size: 0.83rem;
118 color: var(--text-muted);
119 }
120 .search-box {
121 padding: 0.85rem 1.1rem;
122 border-bottom: 1px solid var(--border);
123 }
124 .search-box input {
125 width: 100%;
126 padding: 0.55rem 0.75rem;
127 border-radius: 8px;
128 border: 1px solid var(--border);
129 background: var(--bg);
130 color: var(--text);
131 font-size: 0.88rem;
132 outline: none;
133 }
134 .search-box input:focus {
135 border-color: var(--accent);
136 }
137 .nav-list {
138 list-style: none;
139 padding: 0.75rem 0.65rem 1.5rem;
140 margin: 0;
141 flex: 1;
142 }
143 .nav-item {
144 margin-bottom: 0.25rem;
145 }
146 .nav-btn {
147 width: 100%;
148 text-align: left;
149 background: transparent;
150 border: none;
151 color: var(--text-muted);
152 padding: 0.62rem 0.75rem;
153 border-radius: 8px;
154 font-size: 0.9rem;
155 cursor: pointer;
156 display: flex;
157 align-items: flex-start;
158 gap: 0.55rem;
159 transition: all 0.15s ease;
160 line-height: 1.38;
161 }
162 .nav-btn:hover {
163 background: var(--accent-soft);
164 color: var(--text);
165 }
166 .nav-btn.active {
167 background: var(--accent-soft);
168 color: var(--accent);
169 font-weight: 600;
170 }
171 .nav-num {
172 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
173 font-size: 0.78rem;
174 opacity: 0.8;
175 min-width: 1.5rem;
176 padding-top: 0.08rem;
177 }
178 .sidebar-footer {
179 padding: 0.85rem 1.1rem;
180 border-top: 1px solid var(--border);
181 display: flex;
182 justify-content: space-between;
183 align-items: center;
184 font-size: 0.8rem;
185 color: var(--text-muted);
186 }
187 .theme-btn {
188 background: var(--bg-card);
189 color: var(--text);
190 border: 1px solid var(--border);
191 border-radius: 6px;
192 padding: 0.35rem 0.65rem;
193 font-size: 0.8rem;
194 cursor: pointer;
195 }
196 /* Main content */
197 main.reader {
198 margin-left: 310px;
199 flex: 1;
200 display: flex;
201 justify-content: center;
202 padding: 2.5rem 2rem 5rem;
203 }
204 article.chapter {
205 max-width: 820px;
206 width: 100%;
207 }
208 article.chapter h1 {
209 font-size: 2.15rem;
210 line-height: 1.22;
211 margin-top: 0;
212 margin-bottom: 1.25rem;
213 letter-spacing: -0.025em;
214 border-bottom: 1px solid var(--border);
215 padding-bottom: 0.85rem;
216 }
217 article.chapter h2 {
218 font-size: 1.48rem;
219 margin-top: 2.6rem;
220 margin-bottom: 0.85rem;
221 color: var(--accent);
222 letter-spacing: -0.015em;
223 }
224 article.chapter h3 {
225 font-size: 1.16rem;
226 margin-top: 1.8rem;
227 margin-bottom: 0.6rem;
228 }
229 article.chapter p {
230 margin: 0 0 1.1rem;
231 }
232 article.chapter ul, article.chapter ol {
233 margin: 0 0 1.25rem;
234 padding-left: 1.45rem;
235 }
236 article.chapter li {
237 margin-bottom: 0.4rem;
238 }
239 article.chapter hr {
240 border: none;
241 border-top: 1px solid var(--border);
242 margin: 2.2rem 0;
243 }
244 article.chapter blockquote {
245 margin: 1.35rem 0;
246 padding: 0.9rem 1.15rem;
247 border-left: 4px solid var(--accent);
248 background: var(--accent-soft);
249 border-radius: 0 8px 8px 0;
250 }
251 article.chapter blockquote p:last-child {
252 margin-bottom: 0;
253 }
254 /* Code blocks */
255 .code-wrapper {
256 position: relative;
257 margin: 1.25rem 0 1.5rem;
258 border-radius: 10px;
259 overflow: hidden;
260 border: 1px solid var(--border);
261 background: var(--bg-code);
262 box-shadow: var(--shadow);
263 }
264 .code-header {
265 display: flex;
266 justify-content: space-between;
267 align-items: center;
268 padding: 0.42rem 0.9rem;
269 background: rgba(255, 255, 255, 0.04);
270 border-bottom: 1px solid rgba(255, 255, 255, 0.07);
271 font-size: 0.75rem;
272 color: #94a3b8;
273 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
274 }
275 .copy-btn {
276 background: rgba(255, 255, 255, 0.08);
277 color: #e2e8f0;
278 border: 1px solid rgba(255, 255, 255, 0.14);
279 border-radius: 5px;
280 padding: 0.18rem 0.55rem;
281 font-size: 0.73rem;
282 cursor: pointer;
283 }
284 .copy-btn:hover {
285 background: rgba(56, 189, 248, 0.25);
286 }
287 pre {
288 margin: 0;
289 padding: 1rem 1.15rem;
290 overflow-x: auto;
291 font-family: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
292 font-size: 0.89rem;
293 line-height: 1.58;
294 color: #e6edf3;
295 }
296 p code, li code, td code {
297 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
298 font-size: 0.86em;
299 background: var(--accent-soft);
300 color: var(--accent);
301 padding: 0.15rem 0.38rem;
302 border-radius: 5px;
303 }
304 /* Tables */
305 .table-wrap {
306 overflow-x: auto;
307 margin: 1.35rem 0;
308 border: 1px solid var(--border);
309 border-radius: 10px;
310 }
311 table {
312 width: 100%;
313 border-collapse: collapse;
314 font-size: 0.92rem;
315 }
316 th, td {
317 padding: 0.7rem 0.95rem;
318 border-bottom: 1px solid var(--border);
319 text-align: left;
320 }
321 th {
322 background: var(--bg-sidebar);
323 font-weight: 700;
324 }
325 tr:last-child td {
326 border-bottom: none;
327 }
328 /* Syntax tokens */
329 .rg-com { color: var(--com); font-style: italic; }
330 .rg-kw { color: var(--kw); font-weight: 600; }
331 .rg-ty { color: var(--ty); font-weight: 600; }
332 .rg-str { color: var(--str); }
333 .rg-num { color: var(--num); }
334 .rg-op { color: var(--op); font-weight: 600; }
335 /* Chapter pager */
336 .pager {
337 margin-top: 3.5rem;
338 padding-top: 1.5rem;
339 border-top: 1px solid var(--border);
340 display: flex;
341 justify-content: space-between;
342 gap: 1rem;
343 }
344 .pager-btn {
345 flex: 1;
346 padding: 0.9rem 1.1rem;
347 border-radius: 10px;
348 border: 1px solid var(--border);
349 background: var(--bg-card);
350 color: var(--text);
351 cursor: pointer;
352 text-align: left;
353 transition: border-color 0.15s ease;
354 }
355 .pager-btn.next { text-align: right; }
356 .pager-btn:hover { border-color: var(--accent); }
357 .pager-label {
358 display: block;
359 font-size: 0.76rem;
360 color: var(--text-muted);
361 margin-bottom: 0.2rem;
362 }
363 .pager-title {
364 font-weight: 600;
365 color: var(--accent);
366 font-size: 0.95rem;
367 }
368 .search-results {
369 margin-bottom: 2rem;
370 padding: 1rem 1.2rem;
371 border-radius: 10px;
372 border: 1px solid var(--accent);
373 background: var(--bg-card);
374 }
375 .search-hit {
376 padding: 0.6rem 0;
377 border-bottom: 1px solid var(--border);
378 cursor: pointer;
379 }
380 .search-hit:last-child { border-bottom: none; }
381 .search-hit-title { color: var(--accent); font-weight: 600; }
382 .search-hit-snippet { font-size: 0.86rem; color: var(--text-muted); }
383 @media (max-width: 860px) {
384 aside.sidebar { position: static; width: 100%; border-right: none; border-bottom: 1px solid var(--border); }
385 body { flex-direction: column; }
386 main.reader { margin-left: 0; padding: 1.5rem 1.1rem 3rem; }
387 }
388 </style>
389</head>
390<body>
391 <aside class="sidebar">
392 <div class="brand">
393 <div class="brand-top">
394 <span class="brand-badge">Ridgun v0.9.32</span>
395 <div class="lang-bar"><a class="lang-link" href="../es/index.html">ES</a><a class="lang-link" href="../ca/index.html">CA</a><a class="lang-link" href="../gl/index.html">GL</a><a class="lang-link" href="../eu/index.html">EU</a><a class="lang-link active" href="index.html">EN</a><a class="lang-link" href="../pt/index.html">PT</a><a class="lang-link" href="../sv/index.html">SV</a><a class="lang-link" href="../it/index.html">IT</a></div>
396 </div>
397 <h1>The Ridgun Book</h1>
398 <p>From zero to expert in tabular functional and systems programming</p>
399 </div>
400 <div class="search-box">
401 <input type="search" id="searchInput" placeholder="Search the book (e.g. ||, Tensor, RMM2, TCO...)" />
402 </div>
403 <ul class="nav-list" id="navList"></ul>
404 <div class="sidebar-footer">
405 <span>9 chapters (00–08) · English</span>
406 <button class="theme-btn" id="themeBtn" type="button">☀️ / 🌙 Theme</button>
407 </div>
408 </aside>
409
410 <main class="reader">
411 <article class="chapter" id="chapterContent"></article>
412 </main>
413
414 <script>
415 const CHAPTERS = [{"num":"00","slug":"00-why-do-i-need-ridgun","file":"00-why-do-i-need-ridgun.md","title":"Chapter 00: Why Do I Need Ridgun? Oh Really? I Had No Idea... Tell Me More!","short_title":"Why Do I Need Ridgun? Oh Really? I Had No Idea... Tell Me More!","markdown":"# Chapter 00: Why Do I Need Ridgun? Oh Really? I Had No Idea... Tell Me More!\n\nLet's be honest: when someone tells you *\"we've created a new programming language\"*, your first natural—and very healthy—reaction is to raise an eyebrow and think:\n\n> *\"Another one? Look, I already have Python for moving fast, Rust or C when I need raw speed, and Bash for gluing things together in the terminal. Why on earth would I need to learn Ridgun?\"*\n\nThat is an excellent question. And the short answer is: **because you've gotten used to suffering three daily pains while assuming they were inevitable**.\n\nLet's put a name to them.\n\n---\n\n## Pain #1: \"The Two-World Syndrome\" (Prototype in one, rewrite in another)\n\nYou have almost certainly lived through this movie:\n\n1. You start building a tool or a calculation engine in **Python**, **JavaScript**, or **Ruby** because you want to think about the problem, not about pointers or the compiler.\n2. The code comes out short and clean... until you feed it a 500 MB file or try to use all 16 cores on your machine, and it crawls. And if you want to deploy your program to a clean server, you have to ship half a gigabyte of virtual environments, an interpreter, and dynamic libraries.\n3. Resigned, you decide to rewrite it in **C**, **C++**, or **Rust**. Now it flies and takes up just a few hundred kilobytes, but your 40-line script has ballooned into 300 lines packed with `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, curly braces `{}`, and semicolons `;`.\n\n**What changes with Ridgun?**\nIn Ridgun you write code that is **shorter and cleaner than Python**, yet when you run `ridgunc build`, you get **a single ~350 KB native ELF binary** with no interpreter, no external virtual machine, and no garbage collector. There is no *\"we'll rewrite it in the fast language later\"* phase: the clean code where you think through the problem **is already** the native production binary.\n\n---\n\n## Pain #2: \"Reading someone else's code (or your own from 6 months ago) feels like playing detective\"\n\nLook at how we write conditional logic in almost every mainstream language:\n\n```text\nif (status == ACTIVE && (role == ADMIN || (role == EDITOR && hasPermission))) {\n if (quota > 0) {\n return process(user);\n } else {\n return deny(\"Out of quota\");\n }\n} else {\n return deny(\"Unauthorized\");\n}\n```\n\nOur eyes did not evolve to match nested curly braces or untangle deeply indented `if / else` trees. When engineers, mathematicians, or physicists want to understand complex rules without making mistakes, they don't write paragraphs of `if / else`: **they draw a table**.\n\nIn Ridgun, **the code is that table**:\n\n```ridgun\nauthorize String String Bool Int => String\nauthorize \"active\" \"admin\" _ quota =\n | quota > 0 = \"Processing request\"\n | otherwise = \"Denied: out of quota\"\nauthorize \"active\" \"editor\" True quota =\n | quota > 0 = \"Processing request\"\n | otherwise = \"Denied: out of quota\"\nauthorize _ _ _ _ = \"Denied: unauthorized\"\n```\n\nLook at the columns:\n- Column 1: the status (`\"active\"` or `_`).\n- Column 2: the role (`\"admin\"`, `\"editor\"`, or `_`).\n- Column 3: whether permission is granted (`True` or `_`).\n- Column 4: the `quota`.\n\nYou don't have to mentally execute the program line by line to figure out what happens when an `\"editor\"` comes in with permission `False`: your eyes scan straight down the table and spot the answer in half a second.\n\n---\n\n## Pain #3: \"My computer has 16 cores, but my program only uses 1 (because touching threads terrifies me)\"\n\nToday even a modest phone has 8 cores, and any server or developer laptop has between 12 and 32. Yet 95% of everyday programs still run on a single sequential thread.\n\nWhy? Because in imperative languages, parallelizing means opening Pandora's box:\n- What if two threads mutate the same dictionary at the same time? (*Data race*).\n- What if I lock `Mutex` A and then B in one thread, and B and then A in another? (*Deadlock*).\n- What if I infect my entire codebase with `async` and `await` from top to bottom?\n\nIn Ridgun, **every function is pure and all data is immutable by default**. That has a magical consequence: **two expressions that do not depend on each other can never interfere with each other**.\n\nBecause of that, you don't have to manage threads to use your CPU in Ridgun. You simply declare **what depends on what** using parallel bars `||` and arrows `->`:\n\n```ridgun\nmonthly report Data => Report\nmonthly report d = combine net_metrics disk_metrics cpu_metrics\n || analyze net d -> net_metrics\n || analyze disk d -> disk_metrics\n || analyze cpu d -> cpu_metrics\n```\n\nYou only wrote three clean lines stating where each piece of data comes from. The compiler sees that the three branches are independent and knows it can launch them in parallel without even the slightest possibility of a *data race* or a *deadlock*.\n\nAnd what if you need persistent concurrent agents with an isolated mailbox and state (for example, to maintain thousands of client sessions, background workers, or network connections)? In Ridgun you don't need `Actor` classes or `Mutex` locks: **any ordinary function becomes an autonomous agent in a single line with `agent`**:\n\n```ridgun\ncounter = agent (add visit) 0\nsend 1 to counter\nsend 5 to counter\ntotal = receive from counter\nstop counter\n```\n\nEven better, the compiler performs **smart compile-time strategy selection**: it statically analyzes the depth and computational weight of each function in `||`, `||map`, `||filter`, and `agent`, executing lightweight functions as zero-overhead *green-thread* steps (without paying OS thread warmup costs) and automatically dispatching heavy or deeply recursive computations across real OS kernel threads (`SYS_clone`) on multiple CPU cores.\n\n---\n\n## Pain #4: \"The garbage collector decides to stop the world at the worst possible moment\"\n\nClassic functional languages (Haskell, OCaml, Erlang, Clojure) are wonderful for reasoning about code, but almost all of them rely on a runtime **Garbage Collector**. That rules them out when you need:\n- Microsecond-predictable latency.\n- Exact control over how much RAM an operation is allowed to consume before cleanly aborting it.\n- To write operating system utilities (`ls`, `find`, `sh`, `doas`, `make`) or touch hardware registers (`MMIO`).\n\nRidgun gives you **all the elegance of pure functional programming, backed by Ridgun Memory Management (`RMM`: static `RMM1` and cascading assignment-counted `RMM2`) plus lexical quota-bounded regions (`with region`)**:\n\n```ridgun\nparse packet String => Result\nparse packet raw =\n with region 16_KB do\n decode frame raw\n```\n\nEvery temporary structure is freed deterministically by the compiler (`RMM1` / `RMM2`) the instant it is no longer used. And if an attacker sends you a malicious packet designed to blow up your memory, the moment it crosses byte 16,384 the region halts immediately, frees its entire arena in $O(1)$ time, and returns `Err OutOfMemory`. No process crashes, no GC pauses, and no memory leaks.\n\n---\n\n## In Summary: When Does Ridgun Shine?\n\nRidgun is built for you if you have ever wanted to:\n\n1. **Write systems tools, compilers, parsers, or CLI utilities** that start up in microseconds, weigh a few hundred KB, and cannot crash from null pointers or hidden side effects.\n2. **Build concurrent systems and servers using the Native Agent Model (`agent`, `send ... to`, `receive from`, `stop`)**, where any pure function becomes a stateless or stateful isolated agent and the compiler smartly selects between ultra-lightweight *green-thread* steps and multicore native kernel threads.\n3. **Program algorithms, data pipelines, or tensor algebra** where the compiler verifies even your matrix dimensions (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) before running a single instruction.\n4. **Enjoy reading your own code**: where every `.rg` file looks like a page from a well-typeset book thanks to the automatic tabular formatter (`stable format`).\n\nCurious? Turn to **Chapter 01** and let's write our first program in under two minutes.\n"}, {"num":"01","slug":"01-welcome-to-ridgun","file":"01-welcome-to-ridgun.md","title":"Chapter 01: Your First Contact with Ridgun (No Boilerplate, No Braces, No Semicolons)","short_title":"Your First Contact with Ridgun (No Boilerplate, No Braces, No Semicolons)","markdown":"# Chapter 01: Your First Contact with Ridgun (No Boilerplate, No Braces, No Semicolons)\n\nAlright, Chapter 00 convinced you that we weren't crazy for creating Ridgun. Right now you're probably thinking:\n\n> *\"Nice speech about tables and 16 cores, but show me real code. How long does it take to write something that actually works without fighting the compiler?\"*\n\nLet's get right to it!\n\n---\n\n## 1.1. \"Hello, World!\", But with Teeth\n\nMost programming books show you a bland `print(\"Hello world\")` and turn the page. We are going to jump straight into a function with real logic so you can see how Ridgun breathes from minute one.\n\nCreate a file named `hello.rg` and write this:\n\n```ridgun\ngreet String => String\ngreet \"\" = \"Hello, mysterious traveler! (You didn't tell me your name)\"\ngreet \"Root\" = \"Hey there, Root! Careful what you delete today.\"\ngreet name = \"Hello, {name}! Welcome to Ridgun.\"\n\nmain => ()\nmain =\n echo (greet \"Ada\")\n echo (greet \"Root\")\n echo (greet \"\")\n```\n\nTo run it right now:\n\n```bash\nridgunc run hello.rg\n```\n\nAnd you will see in your terminal:\n\n```text\nHello, Ada! Welcome to Ridgun.\nHey there, Root! Careful what you delete today.\nHello, mysterious traveler! (You didn't tell me your name)\n```\n\nPause for a moment and look at the `greet` block above. Notice what is **NOT** there?\n- **No `function`, `def`, or `pub fn`** (you can put `fn ::` before the signature if you like, but it's optional).\n- **No parentheses `(String name)` or curly braces `{ ... }`** cluttering the screen.\n- **No `if (name == \"\") ... else if (name == \"Root\") ... else ...`**.\n- **No `return`**.\n\n---\n\n## 1.2. How Your Brain Reads an `.rg` File\n\nLet's dissect those four lines of `greet`, because 50% of Ridgun's philosophy is already right here:\n\n```ridgun\ngreet String => String\ngreet \"\" = \"Hello, mysterious traveler! (You didn't tell me your name)\"\ngreet \"Root\" = \"Hey there, Root! Careful what you delete today.\"\ngreet name = \"Hello, {name}! Welcome to Ridgun.\"\n```\n\n1. **The first line is the contract (`greet String => String`)**:\n It says: *\"My name is `greet`, I take a `String`, and I give you back (`=>`) another `String`\"*. Period. If someone tries to call me with the number `42`, the compiler will tap them on the shoulder before the program even runs.\n2. **The lines below are the cases (top to bottom)**:\n - Did you pass me the empty string `\"\"`? I return the first sentence.\n - Did you pass me the exact string `\"Root\"`? I return the second sentence.\n - Did you pass me anything else? I bind it to the variable `name` and interpolate it directly into the string using `{name}`.\n3. **And what is `main => ()`?**:\n Since `main` takes no arguments, we place `=>` right after its name. And it returns `()` (the empty or *Unit* type), because its only job is to print things to the screen with `echo`.\n\n---\n\n## 1.3. \"Wait... do I have to align those spaces by hand like a medieval scribe?\"\n\nNot a chance! If you had to hit the spacebar counting columns by eye, you would quit Ridgun in ten minutes (and rightfully so).\n\nYou type your code quickly, with just a single space between words:\n\n```ridgun\ngreet String => String\ngreet \"\" = \"Hello, mysterious traveler!\"\ngreet name = \"Hello, {name}!\"\n```\n\nAnd when you run **`stable format`** (or press `TAB` / save the file in Emacs with `ridgun-mode`), the canonical formatter places every parameter directly beneath its type and aligns every `=` with pinpoint precision. You write fast; the machine typesets the table.\n\n---\n\n## 1.4. From Quick Script to Battle-Ready Binary in One Command\n\nWhen you run `ridgunc run hello.rg`, you are compiling and launching the program on the fly. But imagine you've finished your tool and want to deploy it to a production server or share it with a teammate who doesn't have Ridgun installed.\n\nYou run:\n\n```bash\nridgunc build hello.rg -o hello\n```\n\nAnd now you have `./hello`: a **native Linux x86_64 ELF executable**, lean, stripped of dead code (*Tree-Shaking*), built with full link-time optimization (*LTO*), and completely independent of any external interpreter.\n\n---\n\n## 1.5. The Honest Cheat Sheet: Where Does Every Idea in Ridgun Come From?\n\nRidgun doesn't try to reinvent the flat tire; it unapologetically borrows the best ideas from four traditions:\n\n| From Whom | What Ridgun Borrows | What It Leaves Behind (For Your Sanity) |\n|---|---|---|\n| **Haskell / ML** | Pure functions, case-by-case *pattern matching*, algebraic data types (`Option`, `Result`), and polymorphism (`A`..`Z`). | Abstract monad transformers, 40 `LANGUAGE` pragmas, and the garbage collector. |\n| **J / APL** | Obsession with tabular code layout, native tensors, and sequence masks (`s(_ + _)`). | Hieroglyphic symbols that are impossible to type on a normal keyboard. |\n| **Rust / C** | Tiny native ELF binaries, zero GC, deterministic memory control (`with region`), and POSIX system calls. | Constant battles with the *borrow checker*, `'a` *lifetimes*, and syntactic verbosity. |\n| **Erlang / Go** | Isolated agents (`agent`, `send ... to`, `receive from`) and channel-based concurrency (`new bounded channel`). | `nil` pointers and accidental shared mutable state. |\n\n---\n\n## 1.6. Most Importantly: What Does Ridgun Do That Nobody Else Has?\n\nGiving credit to the classics is good manners, but if Ridgun were just a \"greatest hits album\" of other languages, you wouldn't be reading this book.\n\nThere are **eight things** we designed and integrated into Ridgun that you won't find together—and many of them not even separately—in any other language today:\n\n### 1. Parallel `||` blocks with `->` that are true parallel graphs, not mere syntactic sugar\nIn other functional languages, a local binding block merely orders expressions that are then evaluated on a single thread.\nIn Ridgun, **a `||` block with `->` arrows is a parallel-execution Directed Acyclic Graph (DAG)** without even needing extra keywords like `where`:\n```ridgun\nannual report Int => Int\nannual report id = revenue - expenses\n || compute sales id -> revenue\n || compute costs id -> expenses\n```\nYou draw the dataflow arrows (`->`). The compiler analyzes what depends on what, **statically verifies that you haven't created any infinite dependency cycles**, and launches independent branches in parallel without you writing a single `async`, `await`, `Future`, or `Mutex`.\n\n### 2. Zero-friction Native Agent Model (`agent`) with smart compile-time thread selection\nIn other languages, programming with actors or agents forces you to define special classes, implement traits, juggle locks, or infect your functions with `async`.\nIn Ridgun, **any ordinary pure function (`fn ::` or `op ::`) turns directly into an autonomous agent—stateless or with isolated state—using `agent`**:\n```ridgun\nserve http (Session, String) => (Session, String)\n...\nserver = agent serve http initial_session\nsend \"GET /profile\" to server\nreply = receive from server\nstop server\n```\nBetter yet: the compiler **statically analyzes function call depth and computational cost at compile time** across `agent`, `||`, and `||map`. If a function is lightweight, it executes as an instant *green-thread* step without paying OS thread warmup overhead; if it is a heavy or deeply recursive computation, it is automatically dispatched in parallel across real OS kernel threads (`SYS_clone`) over your CPU cores.\n\n### 3. Code as a two-dimensional table where the signature is the column header\nIn every other language, the type signature lives on its own line and the function body sits below it like an ordinary paragraph of text.\nIn Ridgun, **the type signature is literally the header of a table**, and every clause below aligns each pattern vertically under its type and each `=` under the `=>`:\n```ridgun\ndivide Int Int => Option\ndivide _ 0 = None\ndivide a b = Some (a / b)\n```\nNo other language treats the signature and its equations as a single visual matrix verified by the compiler and automatically typeset by `stable format`.\n\n### 4. Typed and aligned N-ary phrase operators (*Mixfix* `op ::`)\nIn most languages, you either settle for `+`, `-`, `*` or at best define a cryptic binary operator like `<|>` that nobody can read.\nIn Ridgun you can create **operators that read like natural phrases with as many typed slots as you want** (even using contextual words like `type` or `with`):\n```ridgun\nop :: Int clamped between Int and Int => Int\n x clamped between min and max =\n | x < min = min\n | x > max = max\n | otherwise = x\n```\nAnd then in your code you write `speed clamped between 0 and 120`. It reads like plain English, yet it is 100% statically typed and compiled to native machine code.\n\n### 5. Visual sequence masks (`s(_ + _)` and `-> (+ _ _ +) ->`)\nHave you ever had to write an index salad like `filter(\\(i, x) -> i % 2 == 0)` or unpack a tuple with dummy variables just to keep two specific positions?\nIn Ridgun you pass a **visual mask** directly:\n- `data(_ + _)`: drops the first (`_`), keeps the second (`+`), drops the third (`_`).\n- `parse packet \"v0.9.9\" -> (+ _ _ +) -> combine code -> summary`: filters a 4-element tuple right inside a pipeline and unpacks the 1st and 4th elements directly into the next function.\nZero index arithmetic, zero *off-by-one* bugs.\n\n### 6. \"Zero-noise\" polymorphism (`A`..`Z`) governed by your domain\nIn Rust, TypeScript, or Java, making a function generic means littering the line with angle brackets: `fn swap<A, B>(pair: (A, B)) -> (B, A)`.\nIn Ridgun you simply use a standalone uppercase letter:\n```ridgun\nswap (A, B) => (B, A)\nswap (x, y) = (y, x)\n```\n*\"What if I want a real type in my program called `type A`?\"* — No problem: in Ridgun **your `type` declarations win**. If `type A` does not exist, `A` is a generic type variable; the instant you define `type A = ...`, it becomes your concrete type across the module.\n\n### 7. Zero foreign configuration files: `build.rg` is pure Ridgun\nForget learning TOML for Cargo, JSON for npm, XML for Maven, or the arcane tab-sensitive syntax of a `Makefile`. In Ridgun, the recipe that builds your project (`build.rg`) **is just another `.rg` file**, using the exact same pure functions and *pattern matching* as the rest of your code.\n\n### 8. The \"Holy Trinity\" that seemed impossible: Haskell-level expressiveness + RMM memory without GC or Lifetimes + ~350 KB binaries\nUntil today you had to choose:\n- If you wanted pure functional expressiveness (Haskell, OCaml, Elixir), you paid for it with a garbage collector and a heavy runtime.\n- If you wanted tiny GC-free systems binaries (C, Rust, Zig), you paid for it by juggling raw pointers or wrestling with the *borrow checker* while writing imperative code full of `let mut`.\n\nRidgun breaks that dilemma by combining **Ridgun Memory Management (`RMM`: static `RMM1` and cascading assignment-counted `RMM2`)**, **lexical regions (`with region`)** that free entire arenas in $O(1)$, **tail recursion compiled into native `loop`s**, and **call-graph dead-code elimination (*Tree-Shaking*)** starting from `main`. The result: you write high-level declarative code and get a self-contained **~350 KB** ELF executable ready to go toe-to-toe with tools written in C.\n\nNow that all cards are on the table, let's head to **Chapter 02** and put **`ridgunc`** and **`stable`** to work.\n"}, {"num":"02","slug":"02-getting-started-and-tooling","file":"02-getting-started-and-tooling.md","title":"Chapter 02: Your Two Best Friends: `ridgunc` and `stable` (Zero YAML Files, Promised)","short_title":"Your Two Best Friends: `ridgunc` and `stable` (Zero YAML Files, Promised)","markdown":"# Chapter 02: Your Two Best Friends: `ridgunc` and `stable` (Zero YAML Files, Promised)\n\nSome languages have a wonderful compiler, but a project tooling ecosystem that feels like it was designed by your worst enemy: a 400-line `Makefile` with invisible tabs, an endless `pom.xml`, or a `webpack.config.js` that stops working if you look at it funny...\n\nIn Ridgun we made ourselves a very simple promise on day one:\n\n> *\"There will be only two executables in the entire ecosystem (`ridgunc` and `stable`), both will be written in Ridgun itself, and the project configuration file (`build.rg`) will be ordinary Ridgun code. No JSON, no YAML, no TOML, no weird inventions.\"*\n\nLet's see how to use them.\n\n---\n\n## 2.1. `ridgunc`: The Sniper for Standalone Files\n\nWhen you have a single `.rg` file in front of you and want to test a quick idea, check its types, or compile an executable, you talk directly to **`ridgunc`**.\n\nIt only has 4 commands you actually need to remember:\n\n```bash\n# 1. \"Does this compile, or did I mess up a type?\"\nridgunc check experiment.rg\n\n# 2. \"Run it now!\" (Compiles and runs in one go)\nridgunc run experiment.rg\n\n# 3. \"Build me a standalone native ELF binary to go\"\nridgunc build experiment.rg -o my_program\n\n# 4. \"Emit the intermediate bytecode artifact (.rgc)\"\nridgunc compile experiment.rg -o experiment.rgc\n```\n\n### The Hidden Superpower of `ridgunc check`\nWhen you run `ridgunc check`, it doesn't just tell you *\"0 errors\"*. It also reveals which execution strategy the static analyzer chose for each of your functions:\n- **`InlineSequential`**: *\"This function is small and direct; I'll run it inline with zero scheduling overhead\"*.\n- **`LazyStealableTask`**: *\"There is recursion or a parallel `||` graph here; I'll set it up so other CPU cores can steal work if needed\"*.\n\n### The Scalpel Inside `ridgunc build` (Why Your Binaries Don't Weigh 20 MB)\nWhen you run `ridgunc build`, the compiler doesn't just dump all the code from every library you imported:\n1. It starts at your `main` function and walks the call graph to see **which functions you actually use**. If you imported a massive 80-function library but only call 2 of them, the other 78 are discarded before ever touching the assembler (*Tree-Shaking*).\n2. It finds every tail-recursive function and turns it into a native `loop` that consumes **0 bytes of stack**.\n3. It runs the final link pass with full link-time optimization (*fat LTO*) and strips DWARF debug bloat to leave you with a compact, razor-sharp binary (~350 KB).\n\n---\n\n## 2.2. `stable`: When Your Project Grows and You Want Order\n\nA standalone file is great for a script, but the moment you have multiple modules, shared libraries, and test suites, you need an orchestra conductor. That conductor is **`stable`**.\n\nThe directory layout of a Ridgun project is completely straightforward:\n\n```text\nmy-super-tool/\n├── build.rg # The project manifest (it's Ridgun code!)\n├── src/\n│ ├── main/\n│ │ └── ridgun/\n│ │ ├── lib/ # Your reusable modules (`import utils`)\n│ │ └── app/\n│ │ └── main.rg # Entry point (`main => ()`)\n│ └── test/\n│ └── ridgun/ # Your test suites (`test *.rg`)\n│ └── test all.rg\n└── target/ # Where compiled binaries appear\n```\n\n---\n\n## 2.3. `build.rg`: A Configuration File That Will Never Lie to You\n\nWhy did we refuse to use YAML or TOML to configure a Ridgun project?\nBecause in YAML you misalign two spaces of indentation and don't find out until deployment fails. By contrast, since **`build.rg` is a Ridgun file**, it has static types, gets verified by `stable check`, and formats itself with `stable format`:\n\n```ridgun\nfn :: project name => String\n project name = \"my-super-tool\"\n\nfn :: project version => String\n project version = \"0.1.0\"\n\n-- \"exe\" (executable), \"lib\" (.rglib + .so library), or \"both\"\nfn :: project kind => String\n project kind = \"both\"\n\nfn :: main entry => String\n main entry = \"src/main/ridgun/app/main.rg\"\n\nfn :: source dir => String\n source dir = \"src/main/ridgun\"\n\nfn :: test dir => String\n test dir = \"src/test/ridgun\"\n\nfn :: target dir => String\n target dir = \"target\"\n\nfn :: dependencies => [String]\n dependencies = []\n\nfn :: plugins => [String]\n plugins = []\n\nfn :: task check all => String\n task check all = \"./bin/stable check && ./bin/stable test\"\n```\n\nIf you mistype a field or return type, the compiler tells you immediately. Notice how all setting functions use **natural spaces** (`project name`, `project kind`, `source dir`, `task check all`) instead of underscores.\n\n---\n\n## 2.4. Your Daily Routine with `stable` (The Fantastic Four)\n\nWhen working inside your project, your day-to-day workflow boils down to four commands:\n\n### 1. `stable format` — The End of Team Style Wars\n```bash\nstable format\n```\nYour teammate puts three spaces before `=>` and you put one? Doesn't matter. Run `stable format` and in under a second it leaves **every** `.rg` file in the repository aligned like a Swiss watch.\n\n### 2. `stable check` — Instant Radar\n```bash\nstable check\n```\nWalks every module in `src/main/ridgun/`, verifies types, constructors, arities, and tensor dimensions, and gives you a module-by-module verdict.\n\n### 3. `stable test` — Sleep Soundly at Night\n```bash\nstable test\n```\nFinds all `src/test/ridgun/test *.rg` suites, executes them, and verifies every `assert equals` and `assert true`.\n\n```ridgun\n-- Example of src/test/ridgun/test math.rg\nimport test\n\ndouble Int => Int\ndouble x = x * 2\n\nmain => ()\nmain =\n assert equals (double 21) 42\n assert true ((double 5) > 0)\n```\n\n### 4. `stable built` (and `stable clean`) — Ready for Production\n```bash\nstable built\n```\nCompiles all native targets into `target/`. And if you ever want to wipe `target/` and start fresh: `stable clean`.\n\n---\n\n## 2.5. Building and Using Reusable Libraries (`.rglib` & ELF64 `.so`) and `stable` Plugins\n\nA library module does not need `main => ()`: it exports `type`, `expand type`, `fn ::`, and `op ::` declarations for other projects to import.\n\n### 1. Creating a Library Project with `stable`\n```bash\nstable new --lib geometry\ncd geometry\nstable built lib\n```\nWhen compiling in library mode (`project kind = \"lib\"` or `stable built lib`), the compiler disables `main`-rooted dead-code elimination so **all** exported functions and types are preserved, producing two artifacts in `target/`:\n- **`target/geometry.rglib`**: a self-contained Ridgun library bundle ready to be imported from any project via `import geometry` or `import \"target/geometry.rglib\"`.\n- **`target/libgeometry.so`**: a native Linux ELF64 shared object (`ET_DYN`, for `x86_64` or `arm64-linux`).\n\n```ridgun\n-- src/main/ridgun/lib/geometry.rg (No main function required!)\ntype Point = x: Int, y: Int\n\nfn :: shift point Point Int Int => Point\n shift point p dx dy = Point ((x p) + dx) ((y p) + dy)\n\nfn :: point coords Point => Int Int\n point coords p = (x p, y p)\n```\n\n### 2. How Do I Import a Module or Library? (`import`)\n\nWithout horrific relative paths like `../../../lib/stuff.rg`. If you have a module at `src/main/ridgun/lib/geometry.rg` or a `geometry.rglib` package (in `target/`, `lib/`, or `.stable/deps/` after `stable deps`), write at the top of any file:\n\n```ridgun\nimport geometry\n```\n\nAll functions, operators, and types from `geometry` become available immediately, and Ridgun's canonical loader guarantees each library is included **only once** even across diamond imports.\n\n### 3. Extending `stable` with Custom Tasks (`fn :: task`) and Plugins (`plugins/*.rg`)\n- **In `build.rg`**: declare any `fn :: task <words...> => String` (e.g., `fn :: task serve web => String`) and run it via `stable serve web`.\n- **In `plugins/*.rg` or `.stable/plugins/*.rg`**: drop `.rg` plugin modules that contribute `fn :: task ...` commands or their own `fn :: main => ()` entrypoint, or extend `expand type StableCommand`. Inspect active extensions anytime with `stable tasks` and `stable plugins`.\n\n### 4. Opt-in Strict Mode (`--strict` and `strict mode = True`)\nBy default, neither `ridgunc` nor new projects scaffolded with `stable new` or `stable init` enable strict mode. However, when you want to enforce 100% canonical space-separated Ridgun style across a project, pass `--strict` to `ridgunc` (`ridgunc check --strict`, `ridgunc build --strict`) or set `fn :: strict mode => Bool` (`strict mode = True`) in `build.rg`. Strict mode rejects with a **compilation error**:\n1. Any underscore `_` inside a `fn ::` or `op ::` declaration name (write `fn :: compute total`, never `fn :: compute_total`; local variable names and record field names may still use `_`).\n2. Any parenthesized or comma-separated multi-value return type after `=>` (write `=> Int Int` or `=> RToken [RToken]`, never `=> (Int, Int)`).\n\n---\n\n## 2.6. The Standard and Cryptographic Library Ecosystem (`ridgun-libs`)\n\nRidgun ships with an official suite of **32 pure-Ridgun libraries** (`ridgun-libs`), compiled as `.rglib` bundles and ELF64 `.so` shared libraries ready to import via `import <name>`:\n\n| Family | Libraries (`import ...`) | What They Provide |\n|---|---|---|\n| **1. Text, Formats & Templates** | `str`, `fmt`, `regex`, `diff`, `template`, `json`, `csv`, `ini`, `codec` | String processing, tabular formatting, Thompson NFA regular expressions (`regex`), Myers diff (`diff`), logic-less templates (`template`), `json`/`csv`/`ini` parsers and serializers, and hex/Base64 encoding (`codec`). |\n| **2. Collections, Math & Tensor Algebra** | `list`, `btree`, `math`, `tensor`, `linalg` | Functional list algorithms, ordered in-memory B-trees (`btree`), numerical routines (`math`), typed tensor views/builders (`tensor`), and linear algebra decompositions (`linalg`). |\n| **3. OS, Filesystem & CLI** | `posix`, `io`, `path`, `glob`, `cli`, `time`, `log`, `uuid`, `test` | POSIX descriptors and signals (`posix`), deadline-aware I/O (`io`), path/glob matching (`path`, `glob`), typed CLI flags (`cli`), monotonic clocks (`time`), structured logging (`log`), RFC 4122 `uuid`, and test assertions (`test`). |\n| **4. Cryptography & Security** | `crypto`, `argon2id`, `jwt` | Raw 32-byte binary SHA-256 (`sha256 raw` / `sha256 bytes`), HMAC-SHA-256, PBKDF2-HMAC-SHA-256, HKDF, ChaCha20-Poly1305 AEAD (`crypto`), RFC 9106 `argon2id` password hashing over `Tensor[U64 * 8 * 16]` with zero-allocation BlaMka permutations (`argon2id`), and signed tokens (`jwt`). |\n| **5. Networking, Protocols & Agents** | `socket`, `http`, `ws`, `agent`, `pool`, `net_agent` | POSIX TCP/UDP sockets (`socket`), HTTP/1.1 client/server (`http`), RFC 6455 WebSockets (`ws`), agent supervisors (`agent`), worker pools (`pool`), and reactive agent-driven network servers (`net_agent`). |\n\nNow that you have the toolbox and libraries under control, let's move on to **Chapter 03** to master functions, types, blocks, and string tricks.\n"}, {"num":"03","slug":"03-functions-types-and-alignment","file":"03-functions-types-and-alignment.md","title":"Chapter 03: Functions, Type Signatures, and the Art of Writing Code as a Table","short_title":"Functions, Type Signatures, and the Art of Writing Code as a Table","markdown":"# Chapter 03: Functions, Type Signatures, and the Art of Writing Code as a Table\n\nThere is a question every programmer asks the first time they see functional code:\n\n> *\"If there are no classes, no objects, and no variables that mutate all over the place... where do I put my logic?\"*\n\nIn **functions**. Not those 200-line monster functions tangled with boolean flags that we have all suffered through at some point, but small, direct functions so easy to read they look like a table drawn on a whiteboard.\n\nLet's see how they are built.\n\n---\n\n## 3.1. Two Ways to Present a Function (Pick Your Suit)\n\nIn Ridgun every function begins with its **type signature** (what goes in and what comes out after `=>`), followed below by its **implementation clauses** (with `=`).\n\nYou can write the header in two ways:\n\n### Option A: Straight to the point (no prefix)\n```ridgun\napply discount Int Int => Int\napply discount price percent = price - (price * percent / 100)\n```\n\n### Option B: With an explicit label (`fn ::`)\n```ridgun\nfn :: apply discount Int Int => Int\n apply discount price percent = price - (price * percent / 100)\n```\n\n*\"Hey, why do both exist?\"*\nBecause different people like different styles:\n- Some prefer functions to start flush against the left margin without typing `fn ::` every time (after all, the `=>` arrow already shouts loud and clear that the line is a type signature).\n- Others prefer having a dedicated left-hand column for declaration labels (`fn ::`, `op ::`, `type`, `import`) so they can scan the file visually.\n\nBoth the compiler and `stable format` understand both styles effortlessly.\n\n---\n\n## 3.2. The Magic of Columns: Why Your Eyes Can Relax\n\nLook at this function that calculates shipping costs based on destination country and whether delivery is express:\n\n```ridgun\nshipping cost String Bool Int => Int\nshipping cost \"ES\" False _ = 5\nshipping cost \"ES\" True _ = 12\nshipping cost _ False weight = 10 + (weight * 2)\nshipping cost _ True weight = 25 + (weight * 4)\n```\n\nTry this test: cover the right half of the screen and look only at the three input columns:\n- First column (`String`): `\"ES\"`, `\"ES\"`, `_`, `_`.\n- Second column (`Bool`): `False`, `True`, `False`, `True`.\n- Third column (`Int`): `_`, `_`, `weight`, `weight`.\n\nDo you spot any logical gaps? None: all 4 possible combinations are right in front of your eyes, aligned vertically underneath `String`, `Bool`, and `Int`. In a traditional `if / else` block you would have to read 18 lines just to be sure you didn't miss an edge case.\n\n---\n\n## 3.3. Basic Types and a Gift so You Never Count Zeros Again (`_KB`, `_MB`, `_GB`)\n\nRidgun's scalar types are direct and surprise-free:\n\n| Type | What It Actually Is | Examples |\n|---|---|---|\n| `Int` | Signed 64-bit integer (`i64`) | `0`, `42`, `-100`, `0xFF00`, `1_000_000` |\n| `U64` | Unsigned 64-bit integer (`u64`) for cryptography and systems | `0xdeadbeefcafe0001`, `a xor b`, `x rotr 24` |\n| `Byte` | Unsigned octet (`0..255`) in tensors and binary buffers | `Tensor[Byte * 64]` |\n| `Real` / `Float` | Double-precision float (`f64`) | `3.14159`, `0.001`, `-2.5` |\n| `String` | Immutable text or length-delimited binary buffer | `\"Hello\"`, `'raw\\path'`, `sha256 raw \"abc\"` |\n| `Char` | Character code point | Returned by `str char at` |\n| `Bool` | Classic boolean | `True`, `False` |\n| `()` | Empty (*Unit*, when you only print or perform I/O) | `()` |\n\n### 1-Cycle Bitwise Operators and Lexical Flexibility (`fn`, `op`, `where`)\nFor cryptography, binary protocols, and high-performance systems code, `Int` and `U64` provide native 1-cycle bitwise, shift, and rotation operators:\n- **Logical & bitwise**: `and`, `or`, `xor`, `not`\n- **64-bit shifts & rotations**: `shl` (shift left), `shr` (unsigned logical shift right), `rotl` (rotate left), `rotr` (rotate right)\n\nRidgun's vocabulary is also deliberately minimal and orthogonal:\n- **`where` is not a reserved keyword**: parallel DAG bindings are written exclusively with `||`, so `where` has no special meaning and can be used as a normal identifier.\n- **`fn` and `op` can be part of function and operator names**: after `fn ::` or `op ::`, the words `fn` and `op` can appear freely inside the name (`fn :: op Int => Int`, `op :: Int fn Int => Int`, `op :: A op B => A`).\n- **Space-separated multi-value outputs (`=> Int Int`)**: functions returning multiple values write their output types separated by spaces (`fn :: divmod Int Int => Int Int`), and callers can destructure them without heap-allocating intermediate tuples (`q, r = divmod 17 5`).\n\n### Tired of writing `65536` or `1048576` and praying you didn't drop a digit?\nIn Ridgun, numeric literals directly accept the **`_KB`**, **`_MB`**, and **`_GB`** suffixes, as well as underscores `_` to separate digit groups:\n\n```ridgun\nbuffer size => Int\nbuffer size = 64_KB -- The compiler translates this to 65536\n\nfile limit => Int\nfile limit = 16_MB -- The compiler translates this to 16777216\n\noctet mask => Int\noctet mask = 0xFF_00 -- Clean, readable hexadecimal\n```\n\n---\n\n## 3.4. Double Quotes (`\"...\"`) vs. Single Quotes (`'...'`)\n\nIn many languages, single quotes and double quotes do the exact same thing (a total waste). In Ridgun, each has a crystal-clear mission:\n\n### 1. Double Quotes (`\"...\"`): Smart and Interpolated\nThey interpret escape sequences (`\\n`, `\\t`, `\\\"`, `\\\\`) and **evaluate any variable or expression you place inside curly braces `{...}`**:\n\n```ridgun\norder summary String Int Int => String\norder summary item qty price =\n \"You ordered {qty}x {item}. Total due: {qty * price} euros.\"\n```\n\n*(Pro tip: if you need a literal curly brace inside double quotes—for instance when generating a shell script `\"$\\{VAR:-default\\}\"` or JSON—you can escape it with a backslash `\\{` and `\\}` or double it as `{{` and `}}`).*\n\n### 2. Single Quotes (`'...'`): Raw and Literal (*Raw Strings*)\nWhatever you write inside single quotes comes out **verbatim**, without touching a single backslash `\\` or curly brace `{}`. Perfect when generating code in another language, writing shell commands, or working with backslash-heavy paths:\n\n```ridgun\nsed command => String\nsed command = 's/\\([0-9]+\\)/[\\1]/g'\n```\n\n### Your String Toolbox (`String`)\nJoining strings is as simple as adding them with `+`: `\"Hello, \" + name`. And when you need fine-grained surgery on text, these native functions are ready to go without importing a thing:\n\n- `str len s` $\\rightarrow$ length of `s`.\n- `str char at s i` $\\rightarrow$ numeric code (`Int`) of the character at index `i` (`-1` if out of bounds).\n- `str slice s start end` $\\rightarrow$ slices from `start` up to `end` (excluding `end`).\n- `char from code c` $\\rightarrow$ converts a numeric code (`Int`, e.g. `10` for `\\n` or `34` for `\"`) into a 1-character `String`.\n- `str contains s needle` and `str starts with s prefix` $\\rightarrow$ return `True` or `False`.\n- `int to str n` and `str to int s` $\\rightarrow$ convert numbers to strings and vice versa (`str to int` even understands `\"0xFF\"`!).\n\n---\n\n## 3.5. Multi-Line Blocks, the Universal `=` Rule, and `mut` Variables\n\nWhen a function doesn't fit on a single line, add a newline after `=` and indent the block.\n\nLook at how a block reads step by step:\n\n```ridgun\ncompute invoice Int Int => Int\ncompute invoice base tax_pct =\n taxes = base * tax_pct / 100\n handling_fee = 5\n echo \"Breakdown -> Base:\" base \"Taxes:\" taxes\n base + taxes + handling_fee\n```\n\nFour golden rules make blocks in Ridgun clean and uniform:\n1. **Immutable by default without `let` or `const`**: write `taxes = ...` and you have an immutable local constant. If you accidentally try to overwrite `taxes = 99` further down in the same block, the compiler stops you cold.\n2. **Explicit mutable variables with `mut`**: when you genuinely want to update a local accumulator step by step inside a function, declare it with **`mut`** and then reassign it freely with `=`:\n ```ridgun\n apply surcharges Int => Int\n apply surcharges price =\n mut total = price\n total = total + 10\n total = total * 2\n total\n ```\n *(And if `total` holds a heap-allocated list or structure, Ridgun's **`RMM2`** policy adds `+1` to the new value before subtracting `-1` from the old one, freeing it immediately if nothing else references it).*\n3. **The universal `=` rule**: after **any `=`** (in a function, an operator, a local variable `x =`, or a conditional branch) you can always place any of Ridgun's three block forms:\n - **`(nothing)`**: indented sequential lines (like above).\n - **`|`**: a tree of conditional guards.\n - **`||`**: a parallel computation graph with `->`.\n4. **The last expression is always the return value**: `base + taxes + handling_fee` is what `compute invoice` returns. No `return` keyword needed.\n\nIn **Chapter 04** we will see how to make complex decisions—including multi-level decision trees with `|.`—without ever writing an `if/else` or a `while` loop: welcome to the world of **boolean guards** and **zero-cost tail recursion**.\n"}, {"num":"04","slug":"04-pattern-matching-and-guards","file":"04-pattern-matching-and-guards.md","title":"Chapter 04: Goodbye `if/else`, Goodbye `while` (Pattern Matching, Guards, and TCO)","short_title":"Goodbye `if/else`, Goodbye `while` (Pattern Matching, Guards, and TCO)","markdown":"# Chapter 04: Goodbye `if/else`, Goodbye `while` (Pattern Matching, Guards, and TCO)\n\nWhen you tell someone that Ridgun **has no `if`, no `else`, no `switch`, no `for`, and no `while` keyword**, they usually look at you as if you just said you're going to build a house without a hammer or nails:\n\n> *\"No `if` and no `while`? Then how on earth do I make a decision or loop over a million items?\"*\n\nIt turns out that `if/else` and `while` are fairly blunt tools inherited from how hardware jumps (`GOTO`) worked in the 1950s. In Ridgun we replace them with three pieces that fit together like a glove:\n1. ***Pattern Matching*** (when deciding based on the **shape or exact value** of data).\n2. **Boolean guards `|`** (when deciding based on a **condition or range**).\n3. **Tail recursion with TCO** (which the compiler turns into a blazing-fast native `loop` under the hood).\n\nLet's see them in action.\n\n---\n\n## 4.1. *Pattern Matching*: When the Argument Already Gives You the Answer\n\nWe saw a preview of this in the previous chapter. Instead of taking a generic variable `x` and opening five `if (x == ...)` branches inside the function body, in Ridgun you put the cases right at the front door of the function:\n\n```ridgun\nhttp status Int => String\nhttp status 200 = \"OK: Everything went smoothly\"\nhttp status 301 = \"Moved permanently\"\nhttp status 404 = \"Not found (did you check under the couch?)\"\nhttp status 500 = \"Server is on fire\"\nhttp status n = \"Unknown status code: {n}\"\n```\n\nRidgun tests the rows **from top to bottom**:\n- If `404` comes in, it jumps straight to the third row.\n- If `418` comes in, none of the first four rows match, so it falls through to the last row (`n`), which catches any integer and names it `n` so you can format it.\n- And what if in the last case you don't even care what number it was? Put an underscore `_` (*wildcard*) and forget about it: `http status _ = \"Other status code\"`.\n\n---\n\n## 4.2. Boolean Guards (`|`): Your Scalpel for Ranges, Conditional Bindings, and Decision Trees\n\n*\"Okay, pattern matching is great when I'm looking for `200` or `404`. But what if I want to check whether `score >= 5` or `age > 18 and age < 65`? I'm not going to write 47 rows for every number between 18 and 65!\"*\n\nOf course not! That is what **guards (`|`)** are for.\n\nWhen the result depends on an inequality or a logical combination, right after the `=` of the function (or of any local variable) you open a ladder of vertical bars `|`:\n\n```ridgun\ngrade Int => String\ngrade score =\n | score < 0 = \"Impossible score (negative points?)\"\n | score < 5 = \"Fail\"\n | score < 7 = \"Pass\"\n | score < 9 = \"Merit\"\n | score <= 10 = \"Distinction\"\n | otherwise = \"Off the scale\"\n```\n\nLook closely at how it works:\n- The `=` sign on the header line (`grade score =`) aligns underneath `=>` just like in any other function.\n- Below it, you read top to bottom just like a rate card: each `| condition = result` line is checked in order. The moment one evaluates to `True`, it returns that result and we're done.\n- At the end you place **`| otherwise = ...`**, which means *\"in any other case\"* (your safety net so no path ever falls through without a return value).\n\n### Clear Lowercase Boolean Logic (`and`, `or`, `xor`, `not`)\nHave you ever noticed how many languages use `|` for bits, `||` for booleans, `!` for negation... so when you read a complex condition it looks like a cat walked across your keyboard (`if (!a && (b || c))`)?\n\nIn Ridgun, since `|` is already the sacred symbol for guards and algebraic data types, and `||` is the parallelism operator, **boolean operators are written as clear lowercase words**:\n\n- **`and`**: both sides must hold, using short-circuit evaluation (`age >= 18 and has_license`: if the left side is `False`, the right side is not evaluated).\n- **`or`**: at least one side holds, using short-circuit evaluation (`day == \"saturday\" or day == \"sunday\"`: if the left side is `True`, the right side is not evaluated).\n- **`xor`**: one or the other holds, but not both at once.\n- **`not`**: flips the boolean (`not (is empty text)`).\n- **`<`, `>`, `<=`, `>=`**: compare both numbers (`Int`, `Float`) and **`String` values in lexicographical UTF-8 order** (`\"alpha\" < \"beta\"`).\n\nLook how clean the classic leap-year check becomes:\n\n```ridgun\nis leap year Int => Bool\nis leap year y =\n | (y % 400) == 0 = True\n | (y % 4) == 0 and (y % 100) != 0 = True\n | otherwise = False\n```\n\nZero cryptic symbols. Someone who has never seen Ridgun can read it and understand it on the first try.\n\n### Conditional Local Variables (`x =` + `|`)\nBecause every `=` in Ridgun follows the same universal rule, **you can also use `|` to assign a local variable** without having to extract a separate helper function:\n\n```ridgun\nticket price Int Bool => Int\nticket price base is_large_family =\n discount =\n | is_large_family = 20\n | base > 100 = 10\n | otherwise = 0\n base - (base * discount / 100)\n```\n\n### Nested Decision Trees and the Compact Terminator `|.`\n*\"What if inside a `| cond =` or `| otherwise =` branch I want to open another sub-tree of `|` guards 3 or 4 levels deep, and then return to an outer level?\"*\n\nYou can nest `|` inside `|` as many levels deep as you want. And when a nested `|` block finishes while more branches of a parent level (or more sequential statements in the block) continue below it, you place **`|.`** at the end of its last line (`| otherwise = value |.`) to indicate with zero ambiguity that the sub-tree is closed and you are stepping back to the parent level:\n\n```ridgun\nroute request Int Int Bool Bool Int Int => Int\nroute request method role signed is_admin size_kb limited =\n | method == 0 =\n | size_kb == 0 = 204\n | otherwise = 200 |.\n | method == 1 =\n | not signed = 401\n | otherwise =\n -- Level 2: signed POST request\n | limited > 0 =\n -- Level 3: rate-limit check\n | is_admin =\n -- Level 4: administrator override\n | size_kb > 10000 = 413\n | otherwise = 202 |.\n | otherwise = 429 |.\n | size_kb > 1000 =\n -- Level 3: back from Level 4 thanks to `|.`\n | role >= 2 and is_admin = 201\n | otherwise = 413 |.\n | otherwise = 201 |.\n | otherwise = 405\n```\n\nNotice how clean that is:\n- In a flat 1-level guard tree (or at the very end of the entire function) **you never need to write `|.`** (that would be unnecessary noise).\n- You only write `|.` when closing a nested branch that is followed below by a branch at an outer level.\n\n### Multi-Line Blocks Inside a Guard (`| cond =` + indented block)\nJust as you can open a multi-line indented block after the `=` of a normal function, **after the `=` of any `| cond =` or `| otherwise =` guard you can also add a newline and open a sequential block**:\n\n```ridgun\nadjust window Int Int => Int\nadjust window idx limit =\n | idx >= limit =\n cap = limit - 1\n scaled = cap * 2\n scaled + 10\n | otherwise =\n next = idx + 1\n next * 2\n```\n\nEach guard branch gets its own clean local scope, and its last line is the value returned by that branch.\n\n---\n\n## 4.3. \"No `while`? Won't the Stack Blow Up If I Scan a Large File?\"\n\nLet's address the elephant in the room.\n\nIf you come from Python, Java, or JavaScript, you were taught from day one that *\"recursion looks pretty on a university whiteboard, but in the real world it blows the call stack (`StackOverflowError`) after 1,000 iterations; use a `while` loop\"*.\n\nThat is true in languages that do **not** optimize tail calls. But in a systems functional language like Ridgun, **tail recursion IS a `while` loop dressed up as a pure function**.\n\n### What Is the Difference Between Normal Recursion and Tail Recursion?\nLook at these two functions that count down from `n` to `0` while summing the numbers:\n\n```ridgun\n-- Version A: NORMAL recursion (leaves pending work for the return trip)\nsum slow Int => Int\nsum slow 0 = 0\nsum slow n = n + (sum slow (n - 1))\n\n-- Version B: TAIL recursion (keeps a running total in `acc`)\nsum fast loop Int Int => Int\nsum fast loop 0 acc = acc\nsum fast loop n acc = sum fast loop (n - 1) (acc + n)\n```\n\n- In **Version A**, before it can compute `n + ...`, the function has to wait for `sum slow (n - 1)` to finish. That forces the CPU to push `n` onto the call stack at every step.\n- In **Version B**, the call to `sum fast loop` **is the very last thing that happens on the line**. Nothing is left waiting after it. You pass the updated running total right into `acc + n`.\n\n### `ridgunc`'s Magic Trick (TCO: *Tail-Call Optimization*)\nWhen `ridgunc` compiles `sum fast loop`, it sees that the function calls itself in tail position and says:\n\n> *\"Why would I allocate a new stack frame when the current one is finished? I'll just overwrite `n` with `n - 1`, overwrite `acc` with `acc + n`, and jump back to the top with a native `loop { ... continue; }`.\"*\n\nThe result:\n1. You can call `sum fast loop 100_000_000 0` (one hundred million iterations) and it will consume **exactly 0 extra bytes of stack**.\n2. It runs at the exact same speed as a hand-written `while` loop in Rust or C, without you having to declare a single mutable variable in your Ridgun code.\n\n---\n\n## 4.4. The Master Pattern You Will Use in Every Ridgun Program\n\nWhenever you would think in another language *\"I'll write a `while` loop with an index `i` and an accumulator\"*, in Ridgun you write a pair of functions like this (the clean public function and its tail-recursive `_loop` helper):\n\n```ridgun\nfind char loop String Int Int Int => Int\nfind char loop text pos end target =\n | pos >= end = -1\n | (str char at text pos) == target = pos\n | otherwise = find char loop text (pos + 1) end target\n\nfind char String Int => Int\nfind char text target = find char loop text 0 (str len text) target\n```\n\nLook at the three guards in `find char loop`:\n1. Did I reach the end (`pos >= end`)? Return `-1` (end of loop).\n2. Did I find the character at `pos`? Return `pos` (end of loop).\n3. Not this one? Advance to the next index (`pos + 1`) with a tail call (which the compiler turns into `continue;`).\n\nClean, immune to clobbered-state bugs, and fast as lightning.\n\nIn **Chapter 05** we will get our hands on data collections: **lists, tuples, functions that accept an arbitrary number of arguments (`...`), and J/APL-style sequence masks**.\n"}, {"num":"05","slug":"05-lists-tuples-and-sequences","file":"05-lists-tuples-and-sequences.md","title":"Chapter 05: Lists, Tuples, and the Ninja Trick of Sequences","short_title":"Lists, Tuples, and the Ninja Trick of Sequences","markdown":"# Chapter 05: Lists, Tuples, and the Ninja Trick of Sequences\n\nA program that only moves one number or one string at a time doesn't get you very far. In the real world you handle lists of files, database rows, network packets, or coordinates.\n\nFor that, Ridgun gives you three distinct containers, each designed for a very specific job:\n1. **Lists (`[T]`)**: *\"I have many elements of the same type (or none at all) and I want to traverse them\"*.\n2. **Tuples (`(A, B)`)**: *\"I want to return a pair or trio of different things together without declaring a new type\"*.\n3. **Sequences and masks (`s(_ + _)`, `(+ _ _ +)`)**: *\"I want to filter or slice arguments on the fly as if I had a mathematical sieve\"*.\n\nLet's take them one by one.\n\n---\n\n## 5.1. Lists (`[T]`): Your Daily Bread\n\nIn Ridgun, wrapping square brackets around a type gives you a list of that type:\n- `[Int]` is a list of integers: `[10, 20, 30]`.\n- `[String]` is a list of strings: `[\"apple\", \"pear\"]`.\n- `[]` is the empty list (the wildcard that fits any `[T]`).\n\n> *\"Hey, can I put a number and a string together in the same list like `[1, \"two\"]`?\"*\n> Raw like that, **no** (and thank the compiler for stopping you, because that guarantees an addition will never blow up in production when it stumbles upon a surprise string). In Chapter 06 we will see how to model `[1, \"two\"]` with total elegance using an Algebraic Data Type (just like Haskell and Rust do).\n\n### How to Slice Up a List Without `i`, `j`, `k` Indices: `[]` and `(x:xs)`\nIn imperative languages, walking a list usually means `for (int i = 0; i < list.size(); i++)`, carrying the classic risk of an *off-by-one* error.\n\nIn Ridgun you don't count indices; **you take the list apart from the head**:\n- `[]`: matches when the list is empty.\n- `(x:xs)`: matches when the list has at least one element. It binds the first element to `x` (the **head**) and the rest of the list to `xs` (the **tail**).\n\nLook how satisfying it is to sum a list or count how many elements it has:\n\n```ridgun\nsum list [Int] => Int\nsum list [] = 0\nsum list (x:xs) = x + (sum list xs)\n\ncount elements [A] => Int\ncount elements [] = 0\ncount elements (_:xs) = 1 + (count elements xs)\n```\n\nAnd here is a technical detail that will make your day: in Ridgun, taking the tail `xs` in `(x:xs)` **does not copy the rest of the list in memory**. Internally it is a zero-copy view over the same backing vector that simply advances the start offset by `1` (instantaneous $O(1)$).\n\n### Peeling Off Multiple Elements at Once\nThe `:` pattern shines when you are parsing commands or network protocols:\n\n```ridgun\nparse command [String] => String\nparse command [] = \"You didn't type anything\"\nparse command (\"help\":_) = \"Showing help...\"\nparse command (\"copy\":src:dst:[]) = \"Copying from {src} to {dst}\"\nparse command (cmd:_) = \"Unknown command: {cmd}\"\n```\n\nLook at the third row: `(\"copy\":src:dst:[])` verifies in a single stroke that the list has **exactly 3 elements**, that the first one is `\"copy\"`, and hands you the second and third elements on a silver platter in `src` and `dst`. Try writing that in C or Java and count how many lines of `args.length == 3 && args[0].equals(\"copy\")` you end up with.\n\n### Joining Lists with Double Colon (`::`)\nWhen you are on the right-hand side of `=` (in an expression) and want to concatenate two lists, use **`::`**:\n\n```ridgun\njoin lists [Int] [Int] => [Int]\njoin lists a b = a :: b\n\nappend item [Int] Int => [Int]\nappend item xs x = xs :: [x]\n```\n\n*(Quick mnemonic rule: a single `:` in patterns takes a list apart; a double `::` in expressions glues two lists together).*\n\nAnd what if you genuinely want to grab element number 5 directly without traversing anything? Use square brackets right after the name: `my_list[5]`.\n\n---\n\n## 5.2. Variadic Functions (`T...`): \"Pass Me as Many as You Want!\"\n\nSometimes you want callers of your function to write `max of 4 19 2 88 7` comfortably, without forcing them to wrap the numbers in square brackets `[4, 19, 2, 88, 7]`.\n\nPut three dots **`...`** after the last type in your signature, and inside your function you will automatically receive those arguments packaged as a list:\n\n```ridgun\nfn :: sum many Int... => Int\n sum many nums = sum list nums\n\nfn :: main => ()\n main =\n echo (sum many 10 20 30) -- Prints 60\n echo (sum many 1 2 3 4 5 6 7) -- Prints 28\n```\n\n---\n\n## 5.3. Tuples and Unified Space-Separated Type Signatures (`Int Int [String String] String`)\n\nIn many languages, returning two or more values from a function is a headache: either you create a throwaway helper class `ResultWithPosition`, or you clutter the signature with redundant commas and parentheses.\n\nIn Ridgun, **an argument list `Int String Int` and a tuple `(Int, String, Int)` are conceptually the same thing: a typed sequence (`seq`)**. Because of this, in type signatures you can separate tuple elements simply with **spaces**—after `=>`, inside list brackets `[...]`, and inside parentheses `(...)`:\n\n```ridgun\n-- Two return integers separated by a space:\nfn :: div rem Int Int => Int Int\n div rem a b = (a / b, a % b)\n\n-- Complex return signature without comma or outer-parenthesis clutter:\nfn :: parse header String => Int Int [String String] String\n parse header tag = (200, 2, [(\"Host\", \"ridgun.org\"), (\"X-Tag\", tag)], \"OK\")\n\n-- List of tuples [String String] as an input parameter:\nfn :: lookup header [String String] String => String\n lookup header [] _ = \"missing\"\n lookup header ((k, v):rest) key\n | k == key = v\n | otherwise = lookup header rest key\n```\n\nNotice how clean `=> Int Int [String String] String` is:\n- After `=>`, `Int Int [String String] String` is 100% equivalent to `(Int, Int, [(String, String)], String)`.\n- Inside brackets, `[String String]` denotes a list of string pairs `[(String, String)]`.\n- Inside parentheses (when passing a tuple as a single grouped parameter), `(Int Int)` is 100% equivalent to `(Int, Int)`:\n ```ridgun\n fn :: explain division (Int Int) => String\n explain division (quotient, remainder) = \"Fits {quotient} times with {remainder} left over\"\n ```\n- **Why does this have zero syntactic ambiguity in Ridgun?** Unlike Haskell (where `Maybe Int` applies a type constructor by juxtaposition), parameterized types in Ridgun use explicit parentheses like `Tensor(Float, 3, 3)` and lists use `[T]`. Therefore, two space-separated types (`T1 T2`) always unambiguously denote a tuple/sequence of types while remaining 100% compatible with comma syntax.\n\n### The Star Feature: Automatic Tuple Unpacking Between Functions\nSuppose you have `div rem` (which returns `Int Int`) and another function `add pair Int Int => Int` (which expects 2 separate integers). In Ridgun you can plug one function straight into the other:\n\n```ridgun\nfn :: add pair Int Int => Int\n add pair x y = x + y\n\nfn :: main => ()\n main =\n -- div rem 17 5 returns (3, 2), and add pair receives 3 and 2 directly:\n echo (add pair (div rem 17 5)) -- Prints 5\n```\n\n---\n\n## 5.4. The Ninja Trick: Sequences and Positional Masks (`s(_ + _)`, `(+ _ _ +)`)\n\nHere comes one of Ridgun's most original gems, inherited straight from array languages like J and APL.\n\nImagine you receive a stream of arguments using the sequence pattern **`(s)`** and want to keep only certain positions while discarding others, without inventing five throwaway variable names (`_a`, `b`, `_c`, `d`...).\n\nYou apply a **tuple mask of `+` (keep it) and `_` (drop it)** to `s`:\n\n- **`s(_ + _)`**: out of 3 elements, drop the first (`_`), keep the second (`+`), and drop the third (`_`).\n- **`s(+ _ +)`**: keep the first and the third; returns the sequence containing those two.\n- **`s(_ _ +...)`**: drop the first two (`_ _`) and apply `+...` **to all remaining elements that follow**, no matter how many there are.\n\nSee it in action:\n\n```ridgun\nfn :: middle of three Int Int Int => Int\n middle of three (s) = s(_ + _)\n\nfn :: drop first two Int... => [Int]\n drop first two (s) = s(_ _ +...)\n\nfn :: main => ()\n main =\n echo (middle of three 100 42 999) -- Prints 42\n echo (drop first two 1 2 10 20 30) -- Prints [10, 20, 30]\n```\n\nAnd the best part: you can plug a positional mask **`(+ _ _ +)`** **right inside an idiomatic `->` pipeline**! When a function returns a tuple of several values and the next function only needs two of them, filter the tuple on the fly and Ridgun will unpack the surviving elements directly as separate arguments into the next stage:\n\n```ridgun\n-- parse packet returns 4 values: (Int, Int, [String String], String)\n-- (+ _ _ +) keeps the 1st (code) and the 4th (msg), and unpacks them into combine code:\nparse packet \"v0.9.9\" -> (+ _ _ +) -> combine code -> summary\n```\n\nOn top of that, if you use the comma operator `,` outside parentheses (`list1 , list2`), Ridgun flattens both collections into a single continuous sequence.\n\nIn **Chapter 06** we will take the step that turns a functional programmer into a first-class software architect: **creating your own Algebraic Data Types (ADTs), writing polymorphic functions with `A`..`Z`, and crafting your own custom operators**.\n"}, {"num":"06","slug":"06-algebraic-data-types-and-polymorphism","file":"06-algebraic-data-types-and-polymorphism.md","title":"Chapter 06: Algebraic Data Types (ADTs), Polymorphism, and Why We Don't Want `null` or `Any` Here","short_title":"Algebraic Data Types (ADTs), Polymorphism, and Why We Don't Want `null` or `Any` Here","markdown":"# Chapter 06: Algebraic Data Types (ADTs), Polymorphism, and Why We Don't Want `null` or `Any` Here\n\nIn 1965, Tony Hoare invented the null reference (`null` / `nil`). Forty years later he publicly apologized for it, calling it *\"my billion-dollar mistake\"*.\n\nWhy? Because in languages with `null`, a function promises in its type signature that it will return a `User`... and when you least expect it, it hands you `null` and crashes your production server at three in the morning.\n\nThe exact same problem happens with object inheritance and the `Any` (or `Object`) type: you put a number and a string into a list `[1, \"two\"]`, the language shrugs and says *\"okay, this is now a `List[Any]`, good luck guessing what's inside when you iterate over it\"*.\n\nIn Ridgun we cut both problems off at the root using **Algebraic Data Types (ADTs)** and **Parametric Polymorphism**. It sounds like a intimidating university course title, but in five minutes you will see that it is the most natural way in the world to model data.\n\n---\n\n## 6.1. Product Types (Records): Your Everyday `struct`, But with Superpowers\n\nWhen you want to group several pieces of data that belong to the same entity (for example, a server with its hostname, port, and TLS status), you define a **`type`** with named fields:\n\n```ridgun\ntype Server = host: String, port: Int, tls_enabled: Bool\n```\n\nBy writing that single line, Ridgun automatically gives you **four things** for free:\n\n### 1. The `Server` constructor\n```ridgun\nmy node => Server\nmy node = Server \"api.example.com\" 443 True\n```\n\n### 2. A pure *getter* function for every field (`host`, `port`, `tls_enabled`)\nYou don't write `s.getHost()` or anything like that; each field name is directly a function that extracts that piece of data:\n\n```ridgun\nconnection url Server => String\nconnection url srv = \"Connect to \" + (host srv) + \" on port {port srv}\"\n```\n\n### 3. Functional record update (`with field: value`)\nImagine a configuration record with 12 or 15 fields (like the flags of an `ls` command or a web server) and you want to change just one or two fields without touching the rest. Instead of rebuilding the entire record by passing all 15 fields by hand, you use **`with`** and colons **`:`** (matching the exact syntax of the `type` definition):\n\n```ridgun\nenable dev mode Server => Server\nenable dev mode srv = srv with port: 8080, tls_enabled: False\n```\n\nThe best of both worlds:\n- In your code it is **100% immutable and type-safe** (the compiler verifies that `port` and `tls_enabled` exist on `Server` and have the right types).\n- Under the hood, if `srv` is not shared with anyone else, the native executable reuses its memory *in-place* (`Arc::try_unwrap`) without copying the remaining fields.\n\n### 4. Full *pattern matching* whenever you want to unpack the record directly\n```ridgun\nis secure port Server => Bool\nis secure port (Server _ 443 True) = True\nis secure port _ = False\n```\n\n---\n\n## 6.2. Sum Types (Variants with `|`): How to Retire `null`, Exceptions, and Class Hierarchies\n\nNow for the really good part.\nAn ordinary record has `host` **AND** `port` **AND** `tls_enabled`.\nBut in the real world you often need to say: *\"A payment method is **EITHER** a credit card with its number, **OR** a bank transfer with its IBAN, **OR** cash (which carries no extra data)\"*.\n\nIn object-oriented programming you would have to create a `PaymentMethod` interface, three subclasses in three separate files, constructors, `instanceof` checks... In Ridgun it is **one line**:\n\n```ridgun\ntype PaymentMethod = Card String Int | Transfer String | Cash\n```\n\nAnd look what happens when you process a `PaymentMethod` in a function:\n\n```ridgun\ndescribe payment PaymentMethod => String\ndescribe payment (Card num _) = \"Card payment ending in {num}\"\ndescribe payment (Transfer iban) = \"Bank transfer from account {iban}\"\ndescribe payment Cash = \"Cash payment at the counter\"\n```\n\nEach variant (`Card`, `Transfer`, `Cash`) carries only the data that makes sense for it. It is **impossible** to construct a `Cash` payment with a dangling `iban` attached by mistake.\n\n---\n\n## 6.3. \"So How Do I Put `[1, \"two\"]` in a List Without Using `Any`?\"\n\nLet's come back to the million-dollar question. Imagine you are reading a CSV or JSON file where a cell can be an integer, a text string, or empty.\n\nIf you had an `Any` type, you would stuff `[1, \"two\"]` into a list and lose all compiler assistance: from that point onward, every operation on the list would be walking blindfolded.\n\nIn Ridgun (just like in Haskell or Rust), it takes two seconds to define a sum type that states the exact kinds of values allowed inside that list:\n\n```ridgun\ntype CellValue = Integer Int | Text String | Empty\n\nformat cell CellValue => String\nformat cell (Integer n) = \"Number: {n}\"\nformat cell (Text s) = \"Text: {s}\"\nformat cell Empty = \"(empty)\"\n\nmy row => [CellValue]\nmy row = [Integer 1, Text \"two\", Empty]\n```\n\nSee the difference?\n- With `Any`, the compiler has no idea whether the list holds integers, strings, or a USB-connected coffee maker.\n- With `[CellValue]`, you can freely mix numbers and strings in the same list, **while the compiler knows with 100% certainty that only those three cases exist** and forces you to unpack each case (`Integer n`, `Text s`, `Empty`) before using it. Total safety without sacrificing an ounce of flexibility.\n\n---\n\n## 6.4. `Option` and `Result`: When Something Might Be Missing or Might Fail\n\nBecause Ridgun has neither `null` nor uncaught exceptions flying through the call stack (`throw new Exception()`), the language ships with two built-in universal sum types:\n\n1. **`Option`**: either you have **`Some value`**, or you have **`None`** (nothing).\n2. **`Result`**: either the operation succeeded (**`Ok value`**), or it failed for a specific reason (**`Err error`**).\n\n```ridgun\nsafe divide Int Int => Result\nsafe divide _ 0 = Err \"Hey! Cannot divide by zero\"\nsafe divide a b = Ok (a / b)\n\nprint result Result => ()\nprint result (Ok n) = echo \"The result is {n}\"\nprint result (Err msg) = echo \"Handled failure: {msg}\"\n```\n\nNobody can accidentally ignore an error because the `Result` type itself forces you to check whether there is an `Ok` or an `Err` inside.\n\n---\n\n## 6.5. Parametric Polymorphism: Generic Functions with `A`, `B`, `T`...\n\nSuppose you write a function that swaps a pair `(x, y)` or extracts the first element of a list. Does it really matter whether the list holds `Int`, `String`, or `Server`? Not at all! The algorithm is identical.\n\nIn Ridgun, **any single uppercase letter (`A` through `Z`)** in a type signature automatically acts as a **polymorphic type wildcard (type variable)**:\n\n```ridgun\nsafe head [A] A => A\nsafe head [] default = default\nsafe head (x:_) _ = x\n\nswap pair (A, B) => (B, A)\nswap pair (x, y) = (y, x)\n```\n\nNotice the magic of `safe head [A] A => A`:\n- It is not a lawless `Any`: it states that **the element type of the list `[A]`, the type of the fallback value `default` (`A`), and the return type (`A`) must all be the exact same type**.\n- If you call `safe head [10, 20] 0`, the compiler connects the dots (`A = Int`) and returns an `Int`.\n- If you call `safe head [\"a\", \"b\"] \"none\"`, it connects the dots (`A = String`) and returns a `String`.\n- But if you try to cheat and call `safe head [10, 20] \"none\"`, boom! The compiler stops you cold because the first argument says `A` is `Int` while the second says `A` is `String`.\n\n---\n\n## 6.6. Create Your Own Operators (`op ::`): From Vectors to Custom Phrases\n\nWhy should built-in numbers be the only types allowed to use `+` or `*`? And why settle for standard operators when you can invent the ideal syntax for your domain?\n\nWith **`op ::`** (or without a prefix when using operator patterns) you can pull off two neat tricks:\n\n### 1. Teach `+`, `-`, `*`, `/`, `%` how to operate on your own types\n```ridgun\ntype Complex = real: Int, imag: Int\n\nop :: Complex + Complex => Complex\n a + b = Complex ((real a) + (real b)) ((imag a) + (imag b))\n```\n\n### 2. Create *Mixfix* operators (which read like natural phrases, including `type` and `with`)\nIn an operator signature, **capitalized words (`Int`, `String`, `A`..`Z`) or bracketed/parenthesized types (`[Int]`, `(A, B)`) are the argument slots**, and **lowercase words become part of the operator phrase** (including everyday words like `type` or `with`, which are contextual in Ridgun and never get in your way):\n\n```ridgun\nop :: when Bool then A else A => A\n when True then v else _ = v\n when False then _ else f = f\n\nop :: format String type String with Int => String\n format label type kind with w = \"{label} ({kind}, width={w})\"\n\nmain => ()\nmain =\n message = when (10 > 5) then \"Ten wins!\" else \"Impossible\"\n echo message\n echo (format \"panel\" type \"bordered\" with 80)\n```\n\nYou just built your own polymorphic ternary operator and a formatting DSL in **3 lines** of Ridgun.\n\n---\n\n## 6.7. Shared Constructors, Bare Type Variants, `expand type`, and Natural Spaces in `fn ::`\n\nRidgun makes algebraic data types and functions even more natural and extensible:\n\n1. **Shared constructor names across types**: Multiple `type` declarations can reuse the same constructor names (`Error`, `Var`, `Lit`) with the same or different arities; the typechecker disambiguates them from the signature column type.\n2. **Bare type-name variants without repetition (`type Something = String | Int | MyStruct Float Int`)**: When a sum variant is a built-in type (`String`, `Int`, `Float`/`Real`, `Bool`, `Char`), you do not need to write `Int Int` or `String String`. You can pass bare values (`\"hello\"`, `42`) or explicit constructors (`String \"hello\"`, `Int 42`), and match both literals (`\"admin\"`, `0`) and typed patterns (`(String s)`, `(Int n)`).\n3. **Open type extensions (`expand type`) and automatic `fn ::` clause merging**: Use `expand type` to append new variants to a sum type or new fields to a record. Re-declaring `fn ::` for the same function automatically merges the new pattern clauses **before** any `_` catch-all clause (no `expand fn` keyword needed).\n4. **Natural spaces `\" \"` instead of `_` in `fn ::`, built-ins, and filenames**: Just like `op ::`, multi-word `fn ::` names, built-in calls (`assert equals`, `str len`, `read file`), and `.rg` filenames (`test core language.rg`) use natural spaces:\n\n```ridgun\ntype Something = String | Int | MyStruct Float Int\n\nfn :: describe something Something => String\n describe something \"admin\" = \"special_admin\"\n describe something 0 = \"zero\"\n describe something (String s) = \"str:{s}\"\n describe something (Int n) = \"int:{n}\"\n describe something (MyStruct ratio cnt) = \"struct:{ratio}:{cnt}\"\n describe something _ = \"other\"\n\nexpand type Something = Bool\n\nfn :: describe something Something => String\n describe something (Bool b) = \"bool:{b}\"\n```\n\n### 5. User-Defined Generic Types (`type Result T E`, `type Page T`) and Module Namespaces (`import ... as`, `mod.Type`)\nBeyond polymorphic functions, you can declare **your own generic sum types and generic product records** with uppercase type parameters (`A`..`Z`), and scope identically named types, constructors, fields, and functions across modules without collision (`module ::`, `import db as d`, `http.Config`, `d.Config`). Every module has **strict 3-tier per-module import visibility**: it resolves unqualified names first in **(1) its own declarations** (including multi-file modules sharing `module :: name`), next in **(2) its direct `import` modules**, and only last in **(3) its transitive imports**, ensuring sibling modules or the main application never leak into imported libraries:\n\n```ridgun\ntype Result T E = Ok T | Err E\ntype Page T = items: [T], total: Int\n\nfn :: unwrap ok (Result A String) A => A\n unwrap ok (Ok v) _ = v\n unwrap ok (Err _) d = d\n```\n\nIn **Chapter 07** we will put every core of your processor to work at once without breaking a sweat: **higher-order functions (`map`, `||map`, `filter`, `||filter`, `fold`), lexical closures (`(x => expr)`), parallel `||` graphs with `->`, and concurrent channels**.\n"}, {"num":"07","slug":"07-higher-order-functions-and-dag-parallelism","file":"07-higher-order-functions-and-dag-parallelism.md","title":"Chapter 07: Higher-Order Functions and Sweat-Free Parallelism (No `Mutex`, No `Deadlocks`)","short_title":"Higher-Order Functions and Sweat-Free Parallelism (No `Mutex`, No `Deadlocks`)","markdown":"# Chapter 07: Higher-Order Functions and Sweat-Free Parallelism (No `Mutex`, No `Deadlocks`)\n\nIn almost every programming language there is a dramatic moment in the life of a project—the day someone says in a meeting:\n\n> *\"This is running slow. We need to parallelize it with threads.\"*\n\nSilence falls over the room. Everyone knows what comes next: weeks chasing phantom *race conditions* that only happen on Tuesdays during a full moon, *deadlocks*, and irreproducible bugs.\n\nIn Ridgun, when someone says *\"we need to parallelize this\"*, you simply prefix your list operations with `||` (**`||map`** and **`||filter`**) or drop your calculations into a parallel `||` block with `->` arrows and go grab a coffee.\n\nWhy? Because **when your functions are pure and your data is immutable by default, two CPU cores working side by side can never step on each other's toes**.\n\nLet's walk through it step by step.\n\n---\n\n## 7.1. Functions That Take Functions: `map`, `||map`, `filter`, `||filter`, and `fold`\n\nIn Ridgun a function is just another value. You can pass it as an argument the exact same way you pass the number `7`.\n\nThe language comes out of the box with collection combinators so you can transform lists without writing a single manual recursion, both in sequential mode (`map`, `filter`, `fold`) and in multicore parallel mode using the `||` prefix (**`||map`**, **`||filter`**):\n\n### 1. `map` (sequential) and `||map` (multicore parallel)\nSuppose you have a function `expensive Int => Int` and a list of numbers:\n\n```ridgun\nexpensive Int => Int\nexpensive x = (x * x) + (x * 3)\n\nmain => ()\nmain =\n data = [1, 2, 3, 4, 5, 6, 7, 8]\n -- Strict left-to-right sequential:\n r1 = map expensive data\n -- Multicore parallel with the `||` prefix:\n r2 = ||map expensive data\n echo r1\n echo r2\n```\n\n- With **`map`**, Ridgun applies the function element by element from left to right on the current thread (ideal for small lists or functions with ordered I/O side effects).\n- With **`||map`**, the Ridgun runtime distributes the elements of `data` across your processor's worker threads and reassembles the final list **in the exact same order**, guaranteeing that `||map f xs == map f xs`.\n\n### 2. `filter` (sequential) and `||filter` (parallel): Keeping only elements that match a condition\nYou pass a function that returns `Bool` (a *predicate*) and a list:\n\n```ridgun\nis positive Int => Bool\nis positive n = n > 0\n\nmain => ()\nmain =\n clean = filter is positive [-5, 10, 0, -2, 42]\n clean_par = ||filter is positive [-5, 10, 0, -2, 42]\n echo clean -- Prints [10, 42]\n echo clean_par -- Prints [10, 42]\n```\n\n### 3. `fold`: Reducing an entire list down to a single result\nYou pass a two-argument combining function `(Accumulator Element => Accumulator)`, the initial value, and the list:\n\n```ridgun\nmultiply acc Int Int => Int\nmultiply acc acc x = acc * x\n\nmain => ()\nmain =\n echo (fold multiply acc 1 [2, 3, 4, 5]) -- 1 * 2 * 3 * 4 * 5 = 120\n```\n\n### How Do I Write My Own Function That Accepts Another Function?\nVery easily: in the type signature, wrap the guest function's signature in parentheses `(Input => Output)`:\n\n```ridgun\nrun twice (A => A) A => A\nrun twice f x = f (f x)\n```\n\n### 4. Idiomatic Left-to-Right Pipelines (`->`) — The 100% Ridgun Way!\nWhen you chain several transformations (`map`, `filter`, `fold`, or your own functions and `op ::` operators), nesting parentheses from right to left (`compact l (filter g (map f xs))`) forces you to read inside-out.\n\nIn Ridgun, the **recommended idiomatic style (`2b`)** reads from left to right using the pipeline operator **`->`**, where **whatever comes from the left always enters as the first argument** of the next function or `op ::`:\n\n```ridgun\n-- Recommended idiomatic Ridgun style (2b):\nxs map (x => x * 2) -> filter (x => x > 10) -> fold (acc x => acc + x) 0 -> total\n\n-- And if a function returns a tuple, filter it with a mask and unpack it into the next stage:\nparse packet \"v0.9.9\" -> (+ _ _ +) -> combine code -> summary\n```\n\nPlus, any expression with side effects (writing a file, invoking a system call) can stand on its own line without `echo` or dummy `_ =` variables—or end with `-> ()` when you want to discard its return value explicitly.\n\n### 5. Lexical Closures and Anonymous Functions (`(x => expr)`, `(acc x => expr)`)\nWhen you want to pass a short function on the fly or capture local variables from the enclosing scope (such as an offset, a prefix, or an active session) without declaring a separate top-level helper, use a parenthesized **lexical closure** with **`=>`**:\n\n```ridgun\nfn :: process with session => [Int]\n process with session =\n offset = 10\n bonus = 3\n added = map (x => x + offset) [1, 2, 3]\n filtered = filter (x => x >= bonus) [1, 2, 3, 4]\n total = fold (acc x => acc + x + bonus) 0 [1, 2, 3]\n pairs = map ((a b) => a + b + offset) [(1, 2), (3, 4)]\n added :: filtered :: [total] :: pairs\n```\n\nFive rules keep lambdas clean and unambiguous:\n- **Mandatory parentheses**: every lambda is enclosed in parentheses `(... => ...)`.\n- **Zero-parameter lambdas (`(=> expr)`)**: just as `fn :: main => ()` forbids `()` before `=>`, a zero-parameter lambda is written `(=> expr)` (and its type is `(=> Type)`), never `(() => expr)`.\n- **Multiple parameters vs. tuple (`(a b => ...)` vs. `((a b) => ...)`)**: `(acc x => ...)` takes two separate parameters, whereas `((a b) => ...)` takes a single tuple and destructures it.\n- **Multi-line bodies**: placing a newline after `=>` lets you write an indented multi-step block before the closing `)`:\n ```ridgun\n doubled = map (x =>\n step = x * 2\n step + offset\n ) [1, 2, 3]\n ```\n- **Uppercase vs. lowercase**: `(Conn => Conn)` is a function type (uppercase initial) and `(conn => conn)` is a lambda value (lowercase initial).\n\nCaptured bindings (`offset`, `bonus`) are automatically lifted with deterministic `RMM1`/`RMM2` reference management—zero memory leaks and zero garbage-collector pauses.\n\n---\n\n## 7.2. The Crown Jewel: `||` Blocks with `->` as Parallel Graphs (DAGs)\n\nThis is where Ridgun changes how you think.\n\nIn an ordinary imperative language, when you write three lines in a row:\n1. First line 1 runs.\n2. Then line 2 runs (even if it has nothing to do with line 1).\n3. Then line 3 runs.\n\nThat forces your computer to work wearing blinders. In Ridgun, when you use parallel bars **`||`** with arrows **`->`** (without needing any extra keyword like `where`), you are not issuing sequential commands: you are drawing a **Directed Acyclic Graph (DAG)** that declares which computation feeds which variable:\n\n```ridgun\nfib Int => Int\nfib 0 = 0\nfib 1 = 1\nfib n = (fib (n - 1)) + (fib (n - 2))\n\nfact Int => Int\nfact 0 = 1\nfact n = n * (fact (n - 1))\n\nsuper calc => Int\nsuper calc = left_branch + right_branch\n || fib 10 -> left_branch\n || fact 6 -> right_branch\n```\n\nRead it out loud:\n- *\"I want to compute `left_branch + right_branch`, while in parallel (`||`) `fib 10` flows into `left_branch` (`->`) and `fact 6` flows into `right_branch` (`->`).\"*\n\n### What `ridgunc` Does Behind the Scenes with Your `||` Lines:\n1. It inspects the `->` arrows and builds the dependency graph (DAG).\n2. **It catches cycles statically**: if by mistake you write `|| a + 1 -> b` and `|| b + 1 -> a`, the compiler stops you on the spot with *\"Hold on! There is a circular dependency\"* and refuses to compile an infinite hang.\n3. It sees that `fib 10` and `fact 6` do not depend on each other, so they get the green light to run in parallel before being added in `left_branch + right_branch`.\n\nYou can even build a multi-stage pipeline inside the same `||` block—either attached to the bottom of an expression or right after `=`—and the compiler will figure out all by itself which stages can run simultaneously and which ones must wait:\n\n```ridgun\nquarterly balance Int => Int\nquarterly balance base =\n || base * 100 -> gross_sales\n || base * 40 -> fixed_costs\n || gross_sales - fixed_costs -> gross_profit ||.\n gross_profit * 80 / 100\n```\n\nNotice the **`||.`** terminator at the end of the last `||` line: just as `|.` closes a nested guard tree, **`||.`** closes the parallel `||` block when normal sequential statements continue below it. Here `gross_sales` and `fixed_costs` start in parallel; once both finish, `gross_profit` is computed, and finally `gross_profit * 80 / 100` is returned. All without a single `async`, `Future`, `Promise`, or `Mutex`.\n\n---\n\n## 7.3. The Nameless `||` Shortcut: Parallel Pipelines That Return a Tuple\n\nWhat if you just want to launch two or three calculations in parallel and collect them together into a tuple without even inventing variable names with `->`?\n\nUse `||` directly:\n\n```ridgun\nparallel pair Int => (Int, Int)\nparallel pair n =\n || fib n\n || fact n\n```\n\nDirect, visual, and it returns the tuple `(fib n, fact n)`.\n\n---\n\n## 7.4. When You Want Manual Steering: the Native Agent Model (`agent`, `send ... to`, `receive from`) and CSP Channels\n\nAutomatic parallelism via `||` blocks and `||map` covers 90% of real-world cases. But when you are building a persistent server, isolated user sessions, a job queue, or a producer-consumer pipeline, you want **concurrent agents with isolated memory and statically typed message passing**.\n\nIn Ridgun there is no need for a special `actor ::` declaration keyword: **any normal function (`fn ::` or `op ::`) becomes a concurrent agent in a single line with `agent`**:\n\n### 1. Stateless and stateful agents (`agent`, `send ... to`, `receive from`, `stop`)\n- `ag = agent (my function)`: wraps a function `M => R` into a stateless agent of type `Agent M R`.\n- `ag = agent (stateful function) initial_state`: wraps a function `S M => S` (or `S M => S R`) into an agent with isolated state that remembers its state across messages without a single `Mutex`.\n- `send msg to ag` (or with multiple arguments: `send a b to ag`): sends a typed message to the agent's inbox.\n- `res = receive from ag` (or non-blocking `try receive from ag`): pulls the next reply from the agent's outbox.\n- `stop ag`: terminates the agent and deterministically frees its state via `RMM1`/`RMM2`.\n\n```ridgun\nfn :: add visit Int Int => Int\n add visit acc n = acc + n\n\nfn :: process batch Int Int => Int\n process batch id t = (id * 100) + t\n\nfn :: main => ()\n main =\n counter = agent (add visit) 10\n send 5 to counter\n send 7 to counter\n v1 = receive from counter\n v2 = receive from counter\n stop counter\n\n worker = agent (process batch)\n send 3 25 to worker\n batch = receive from worker\n stop worker\n echo \"Visits:\" v1 \"->\" v2 \"| Batch:\" batch\n```\n\n### 2. Smart compile-time strategy selection (*Green Threads* vs. Real OS Kernel Threads)\nA common question is: *\"Do `||`, `||map`, and `agent` always spawn OS threads, or do they run as lightweight green tasks?\"*\n\nIn Ridgun, **the compiler makes that decision for you by statically analyzing each function**:\n- **Lightweight functions (`TierTrivial` / `StratInlineSequential`)**: If the function passed to `||`, `||map`, `||filter`, or `agent` has shallow depth (arithmetic, string formatting, fast HTTP session state transitions), warming up OS threads would cost more than the computation itself. The compiler executes it as an ultra-lightweight *green-thread* step with zero `SYS_clone` overhead.\n- **Heavy or deeply recursive computations (`TierDynamicRecursive` / `TierHeavyCPU`)**: If the function has deep recursion (like `fib` or `fact`) or tensor algebra (`einsum`, `hadamard`, `@`), the compiler automatically emits multicore dispatch across **real OS kernel threads (`SYS_clone`)**, processing `||` branches or queued `agent` messages in true hardware parallelism across your CPU cores.\n\n### 3. Passing messages through bounded channels (`new bounded channel`)\nJust like in Go or Erlang, in addition to agents you can also create explicit thread-safe pipes to connect pipeline stages:\n\n- `new bounded channel capacity`: creates a channel with room for `capacity` messages.\n- `send through value channel` (or `chan send value channel`): puts a message into the pipe.\n- `receive from channel` (or `chan recv channel`): pulls the next message from the pipe.\n- `try receive from channel`: checks whether a message is waiting without blocking; returns `Some value` or `None`.\n- `receive from timeout channel ticks`: waits up to a time limit and returns `Ok value` or `Err Timeout`.\n\n```ridgun\nfn :: main => ()\n main =\n pipe = new bounded channel 4\n _ = send through \"Task A completed\" pipe\n _ = send through \"Task B completed\" pipe\n first = receive from pipe\n second = receive from pipe\n echo first \"|\" second\n```\n\n### 4. Fire-and-forget sink agents (`agent sink`), global mailboxes (`agent ::`), and deterministic testing (`with agent`)\nWhen you need asynchronous audit logs, metrics, tracing, or cross-cutting rate limiting without threading an extra handle through every function signature or accumulating unread replies in an output queue, Ridgun provides **sink agents (`agent sink`)** and **top-level global mailboxes (`agent ::`)**:\n- `rec = agent sink (step fn) [init_state], mailbox N drop|block, drain 2_s`: processes messages in FIFO order (`S M => S` or `M => ()`), releases old states via `RMM1`/`RMM2`, and has **no output queue** (`receive from`, `try receive from`, and `ask` on a `SinkAgent` are compile-time errors).\n- `agent :: audit_log = sink (step fn) [], mailbox 10_000 drop, drain 2_s`: declares a module-level sink agent initialized before `main` and drained in reverse declaration order at program exit.\n- `agent dropped ag`, `agent flush ag`, `agent state ag`, and `with agent audit_log = spy do ...`: let you inspect backpressure drops, flush pending messages, inspect state snapshots, and redirect a global mailbox to an isolated spy agent in tests.\n- In addition, `Channel`, `Agent`, and `SinkAgent` descriptors can be shared across multiple workers (`send ch 1 to w1`, `send ch 2 to w2`), while regular heap payloads sent through channels remain affine (`ErrUseAfterSend`).\n\nAnd now, fasten your seatbelt for **Chapter 08**, the grand finale: **tensors with compile-time verified dimensions, scoped variables (`context`/`shared`), deterministic memory regions, direct MMIO hardware access, and UNIX/POSIX systems programming**.\n"}, {"num":"08","slug":"08-tensors-rmm-regions-and-systems","file":"08-tensors-rmm-regions-and-systems.md","title":"Chapter 08: Tensors, RMM Compiled Memory (`RMM1`/`RMM2`), Regions, and Systems","short_title":"Tensors, RMM Compiled Memory (`RMM1`/`RMM2`), Regions, and Systems","markdown":"# Chapter 08: Tensors, RMM Compiled Memory (`RMM1`/`RMM2`), Regions, and Systems\n\nSo far we have seen the friendly, elegant, mathematical side of Ridgun: pure functions, algebraic data types, *pattern matching*, decision trees with `|` / `|.`, and parallel `||` graphs with `->` that put every core of your processor to work without asking you for a single `Mutex`.\n\nAt this point, a veteran systems programmer at the back of the room usually raises a hand with a half-smile and asks:\n\n> *\"All of this is lovely for computing invoices or compiling ASTs, but what happens when I need to multiply matrices of millions of floats at breakneck speed? What about memory? Is there a Garbage Collector hiding under the rug waiting for the worst possible moment to freeze my program for 200 milliseconds? And how do I talk to a real Unix operating system?\"*\n\nExcellent questions. Let's pop the hood of Ridgun and see how it handles low-level systems work, numerical computing, and deterministic memory.\n\n---\n\n## 8.1. The Memory Dilemma: Neither Garbage Collector Nor Borrow Checker Fights (`RMM1` and `RMM2`)\n\nIn modern programming languages there are usually two irreconcilable camps:\n\n1. **The Garbage Collector camp (Java, Go, Python, JS, Haskell, OCaml):** you allocate memory freely and, every now and then, an invisible garbage truck stops all traffic in your program to trace which objects are still alive and which are dead. It is wonderfully convenient... until you are building an ultra-fast systems tool or a strict-latency server and the garbage truck decides to double-park.\n2. **The manual control / Borrow Checker camp (C, C++, Rust):** there is no garbage collector, but in exchange you either juggle raw pointers by hand (risking a `Segmentation fault` or *use-after-free*) or spend half the afternoon arguing with the *borrow checker* about why `'a` does not live as long as `'b`.\n\nRidgun solves this dilemma at the root with **Ridgun Memory Management (`RMM`)**, a deterministic system of two complementary policies (**`RMM1`** and **`RMM2`**) combined with **lexical regions (`with region`)**:\n\n### Policy `RMM1` — Compiled Memory Management (Linear and Conditional CMM)\nWhen the compiler statically knows the flow of local variables inside a function:\n- **Linear CMM**: `ridgunc` computes at compile time the **exact last line** where each local variable (`a`, `b`, ...) is read and inserts its deterministic deallocation (`rg_cmm_drop`) immediately afterward, or transfers ownership with zero copies (`rg_cmm_move`) when the variable is returned as the result.\n- **Conditional CMM (`ptr(a) != ptr(c)`)**: what if a function like `choose cond a b` returns either `a` or `b` depending on a runtime condition? `ridgunc` builds the ancestor table and emits a **1-CPU-cycle** pointer-identity guard: when `b` is no longer used while `c` remains alive, it checks `if ptr(b) != ptr(c) { free(b) }`. The unchosen branch is freed immediately and the chosen one survives intact.\n\n### Policy `RMM2` — Assignment Counting and Structural Cascade\nAnd what happens when you nest data structures inside other data structures (`xs = [a, b]`), reassign mutable variables (`mut x = ...`), or chain complex calls? That is where **`RMM2`** steps in, governing the entire lifecycle through four airtight mathematical rules:\n\n1. **Incoming function arguments are untouched (`+0` on entry, `-0` on exit)**: passing a list or a record into a function never increments or decrements reference counters needlessly.\n2. **Every assignment to a local variable or container slot adds `+1`**:\n - `a = [10, 20]` $\\rightarrow$ block `M1` is born with counter `1`.\n - `xs = [a, b]` $\\rightarrow$ list `xs` is born with `1`, and because it holds references to `a` and `b`, both climb to `2`.\n - If you reassign a mutable variable (`mut x = ...` and later `x = new_val`), `RMM2` applies **`+1`** to the new value first before applying **`-1`** to the old one.\n3. **Upon exiting the function, local variables that are NOT returned receive `-1`**: if a structure's counter reaches `0`, its memory is freed on the spot and **it cascades `-1`, one by one, to each of its direct children**.\n4. **The value returned by the function is exempt in transit**: it receives its local-variable `-1` without being destroyed as it crosses the return boundary, so when the caller writes `c = my func ()`, the `+1` from that assignment leaves its counter at `1`.\n\n```ridgun\nchoose Bool [Int] [Int] => [Int]\nchoose cond a b =\n | cond = a\n | otherwise = b\n\nrmm2 example Bool => Int\nrmm2 example cond =\n a = [10, 20, 30] -- M1 (rc = 1)\n b = [40, 50, 60] -- M2 (rc = 1)\n c = choose cond a b -- `c` gets +1; the unchosen list dies right here (rc -> 0)\n (count a) + (count c) -- On exit, `a` and `c` get -1 and 0 live blocks remain\n```\n\n---\n\n## 8.2. Quota-Bounded Lexical Regions (`with region`): $O(1)$ Memory Firewalls\n\nAlongside `RMM1` and `RMM2`, whenever you are parsing an external network packet, a massive file, or a batch of untrusted data and want to enforce a **strict RAM ceiling**, you wrap the computation in a **`with region`** block:\n\n```ridgun\nprocess frame String => Result\nprocess frame raw =\n with region 64_KB do\n clean = map clean line (split lines raw)\n count valid clean\n```\n\n1. **Hardware/runtime-guaranteed quota**: if code inside the block attempts to allocate even a single byte beyond `64_KB` (`65,536` bytes), the runtime cleanly aborts the block, frees the entire temporary arena in constant $O(1)$ time, and returns `Err OutOfMemory` without crashing the process.\n2. **Instantaneous deallocation on exit**: the moment the `with region` block finishes, all temporary memory in the arena vanishes in one stroke with zero fragmentation.\n\n---\n\n## 8.3. Native Tensors, Einstein Contraction (`@`), and Symbolic Dimensions\n\nIf you have ever programmed in Python with NumPy or PyTorch, you have almost certainly suffered the classic crash 41 minutes into a training run:\n\n```text\nRuntimeError: mat1 and mat2 shapes cannot be multiplied (128x64 and 32x128)\n```\n\nIn Ridgun, **`Tensor`** values are first-class native citizens and their dimensions are part of the type signature:\n\n```ridgun\n-- If (N × K) and (K × M) go in, (N × M) MUST come out!\nmultiply matrices Tensor[N, K] Tensor[K, M] => Tensor[N, M]\nmultiply matrices a b = a_ik @ b_kj\n```\n\nNotice three standout details:\n1. **Static dimension verification (`Tensor[N, K]`)**: the inner axis `K` of the first matrix must match the leading axis `K` of the second. If you try to pass incompatible dimensions, `ridgunc check` stops you before executing a single instruction.\n2. **Einstein index notation (`a_ik @ b_kj`) and Hadamard broadcasting (`a_ij * b_j`)**:\n - With **`@`**, indices repeated across all operands (`k`) are contracted (summed over) while unique indices (`i`, `j`) survive in the output tensor.\n - With **`+`, `-`, `*`, `/`**, indices align dimensions and perform automatic *broadcasting* (`a_ij * b_j` multiplies each row `i` of `a` by vector `b_j` without needing `unsqueeze` or `reshape`).\n3. **Typed Tensors (`Tensor[T * d1 * d2]`), Mathematical Subscripts `t_(i, j)`, Slice Views `t_(i,)`, and *In-Place* Axis Mapping (`in`)**:\n You can allocate zero-initialized tensors with explicit element types (`U64`, `Int`, `Float`, `Byte`), index them with mathematical subscripts `t_(i, j)`, take zero-copy row (`t_(i,)`) or column (`t_(,j)`) views, destructure multi-return calls directly into tensor elements with zero heap allocations, and map functions in-place along an axis using `map ... in` or `||map ... in`:\n ```ridgun\n fn :: blamka p16 Tensor[U64 * 16] => ()\n blamka p16 v =\n v_(0), v_(4), v_(8), v_(12) = blamka g v_(0) v_(4) v_(8) v_(12)\n\n fn :: compress block Tensor[U64 * 8 * 16] Tensor[U64 * 8 * 16] => Tensor[U64 * 8 * 16]\n compress block x y =\n r_ij = x_ij xor y_ij\n z_ij = r_ij\n ||map blamka p16 in z_(i,)\n ||map blamka p16 in z_(,j)\n z_ij xor r_ij\n ```\n\n---\n\n## 8.4. Hardware Access (`hardware ::`) and POSIX Systems Primitives\n\nThe `ridgunc` compiler itself, the `stable` build manager, and Unix systems utilities are written in Ridgun. To make that possible, the language includes memory-mapped register blocks (`hardware ::`) and direct POSIX primitives:\n\n```ridgun\nhardware :: UART0 @ 0x1000_0000\n tx_data : Int @ 0x00\n status : Int @ 0x04\n```\n\nAnd for interacting with the operating system:\n\n| Primitive | Signature | What It Does |\n|---|---|---|\n| `cli args` | `=> [String]` | Command-line arguments (`argv`). |\n| `read file` / `write file` | `String => String` / `String String => ()` | Full file reading and writing on disk. |\n| `read bytes` / `write bytes` | `String => [Int]` / `String [Int] => ()` | Exact byte-by-byte binary reading and writing (`0..255`). |\n| `list dir` / `file lstat` | `String => [String]` / `String => ...` | Directory listing and POSIX metadata inspection (`lstat`). |\n| `sha256 file` / `sha256 str` | `String => String` | Native 64-character hex SHA-256 cryptographic hash. |\n| `sha256 raw` / `sha256 bytes` | `String => String` | Raw 32-byte binary SHA-256 digest (supports embedded `0x00` bytes with zero hex conversion overhead). |\n| `sys exec` / `shell exec` | `... => Int` | Operating system process execution. |\n| `sys getcwd` / `sys chdir` | `=> String` / `String => Int` | Query or change the current working directory. |\n| `sys getenv` / `sys setenv` | `String => String` / `String String => Int` | Process environment variables. |\n| `sys getuid` / `sys getgid` | `=> Int` | Real process UID and GID (`doas`, security tools). |\n| `sys exit` | `Int => ()` | Terminate the process with an exit code. |\n\n---\n\n## 8.5. Real-World Case Study: Building `rg-wc`, a Parallel Unix Utility\n\nLet's bring together everything we have learned from Chapter 01 to Chapter 08 in a real systems program: a parallel version of the Unix `wc` utility (line and byte counter across multiple files) written in pure Ridgun:\n\n```ridgun\n-- 1. Model file metrics using an ADT record (Chapter 06)\ntype Metrics = file: String, lines: Int, bytes: Int\n\n-- 2. Count newline characters (ASCII code 10) with TCO tail recursion (Chapter 04)\nfn :: count newlines String Int Int Int => Int\n count newlines text pos end acc =\n | pos >= end = acc\n | (str char at text pos) == 10 = count newlines text (pos + 1) end (acc + 1)\n | otherwise = count newlines text (pos + 1) end acc\n\nfn :: line count String => Int\n line count text = count newlines text 0 (str len text) 0\n\n-- 3. Analyze a file and return its Metrics record\nfn :: analyze file String => Metrics\n analyze file path =\n content = read file path\n || line count content -> n_lines\n || str len content -> n_bytes ||.\n Metrics path n_lines n_bytes\n\n-- 4. Format each row and sum totals using `fold` and `||` (Chapter 07)\nfn :: format metric Metrics => String\n format metric m = \"{lines m}\\t{bytes m}\\t{file m}\"\n\nfn :: sum lines Int Metrics => Int\n sum lines acc m = acc + (lines m)\n\nfn :: sum bytes Int Metrics => Int\n sum bytes acc m = acc + (bytes m)\n\nfn :: generate report [String] => String\n generate report paths =\n metrics = ||map analyze file paths\n || map format metric metrics -> rows\n || fold sum lines 0 metrics -> total_l\n || fold sum bytes 0 metrics -> total_b ||.\n \"{rows}\\nTotal: {total_l} lines, {total_b} bytes\"\n\nfn :: main => ()\n main =\n echo (generate report [\"README.md\", \"AGENTS.md\"])\n```\n\nPause for a second and look at those ~40 lines of code:\n- There is not a single `null`, unchecked exception, or `Mutex`.\n- `count newlines` compiles down to a native `loop` using 0 bytes of stack thanks to automatic TCO.\n- `||map analyze file paths` processes files in parallel across all cores of your CPU, and the `||` block computes the formatted rows, total lines, and total bytes simultaneously.\n- Temporary memory is freed deterministically via **`RMM1`/`RMM2`**.\n- And when you run `ridgunc build rg_wc.rg -o rg-wc`, you get a self-contained ~350 KB native binary ready to copy into `/usr/local/bin`.\n\n---\n\n## 8.6. Final Roadmap: The 10 Golden Rules of the Ridgun Programmer\n\n1. **Always write the contract (`=>`) first and the equations (`=`) second:** thinking about types first makes the compiler work for you.\n2. **Let `stable format` typeset your tables:** type with single spaces and let the tool align columns, `=>`, `=`, and `->`.\n3. **Remember the universal `=` rule:** after any `=` you can open a sequential block, a `|` guard tree, or a parallel `||` graph.\n4. **Use `|.` and `||.` only when closing a nested block to return to an outer level:** keep deep decision trees compact and unambiguous.\n5. **Immutable by default (`x = ...`), explicitly mutable (`mut x = ...`):** reach for `mut` only when a local accumulator genuinely calls for it.\n6. **Use clear lowercase boolean operators (`and`, `or`, `xor`, `not`) and always close your guards with `| otherwise =`.**\n7. **Model your domain with ADTs (`type`):** use records with `with field: value` and sum types (`Option`, `Result`) instead of `null`.\n8. **Parallelize by declaring dependencies with `|| expr -> var` and `||map` / `||filter`:** let the compiler verify acyclicity and distribute work across cores.\n9. **Give your Tensors symbolic dimensions (`Tensor[N, K]`) and your contractions explicit indices (`a_ik @ b_kj`).**\n10. **Trust `RMM` (`RMM1`/`RMM2`), bound untrusted inputs with `with region`, and always verify with `stable check` and `stable test`.**\n\nHappy hacking in Ridgun!\n"}];
416 const UI_STRINGS = {"copy_label":"Copy","copied_label":"Copied!","prev_label":"&larr; Previous chapter","next_label":"Next chapter &rarr;","search_hits_label":"Results for","search_empty_label":"No matches found for"};
417 let currentIdx = 0;
418
419 function escapeHtml(s) {
420 return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
421 }
422
423 function highlightRidgun(code) {
424 return code.split("\n").map(line => {
425 const comIdx = (() => {
426 let inD = false, inS = false;
427 for (let i = 0; i < line.length - 1; i++) {
428 if (line[i] === "\\") { i++; continue; }
429 if (line[i] === '"' && !inS) inD = !inD;
430 else if (line[i] === "'" && !inD) inS = !inS;
431 else if (!inD && !inS && line[i] === "-" && line[i+1] === "-") return i;
432 }
433 return -1;
434 })();
435 const codePart = comIdx >= 0 ? line.slice(0, comIdx) : line;
436 const comPart = comIdx >= 0 ? line.slice(comIdx) : "";
437 let html = escapeHtml(codePart);
438 // Strings
439 html = html.replace(/("([^"\\]|\\.)*"|'[^']*')/g, '<span class="rg-str">$1</span>');
440 // Keywords
441 html = html.replace(/\b(fn|op|type|import|mut|otherwise|with|region|do|global|context|shared|atomic|hardware|echo|and|or|xor|not)\b/g, '<span class="rg-kw">$1</span>');
442 // Types & constructors
443 html = html.replace(/\b(Int|Real|String|Char|Bool|Tensor|Option|Result|Some|None|Ok|Err|OutOfMemory|Timeout|True|False)\b/g, '<span class="rg-ty">$1</span>');
444 // Operators
445 html = html.replace(/(::|=&gt;|-&gt;|&lt;-|\|\|\.|\|\||\|\.)/g, '<span class="rg-op">$1</span>');
446 // Numbers & byte units
447 html = html.replace(/\b(0x[0-9a-fA-F_]+|[0-9][0-9_]*(_KB|_MB|_GB)?(\.[0-9]+)?)\b/g, '<span class="rg-num">$1</span>');
448 if (comPart) {
449 html += '<span class="rg-com">' + escapeHtml(comPart) + '</span>';
450 }
451 return html;
452 }).join("\n");
453 }
454
455 function formatInline(text) {
456 let out = escapeHtml(text);
457 out = out.replace(/`([^`]+)`/g, "<code>$1</code>");
458 out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
459 out = out.replace(/\*([^*]+)\*/g, "<em>$1</em>");
460 out = out.replace(/\$O\(([^)]+)\)\$/g, "<code>O($1)</code>");
461 out = out.replace(/\$\\rightarrow\$/g, "&rarr;");
462 out = out.replace(/\$1024\^2\$/g, "1024<sup>2</sup>");
463 out = out.replace(/\$1024\^3\$/g, "1024<sup>3</sup>");
464 out = out.replace(/\$1024\$/g, "1024");
465 return out;
466 }
467
468 function renderMarkdown(md) {
469 const lines = md.split("\n");
470 let html = "";
471 let i = 0;
472 while (i < lines.length) {
473 const line = lines[i];
474 // Fenced code block
475 if (line.startsWith("```")) {
476 const lang = line.slice(3).trim() || "ridgun";
477 i++;
478 const buf = [];
479 while (i < lines.length && !lines[i].startsWith("```")) {
480 buf.push(lines[i]);
481 i++;
482 }
483 i++; // skip closing ```
484 const rawCode = buf.join("\n");
485 const bodyHtml = (lang === "ridgun" || lang === "") ? highlightRidgun(rawCode) : escapeHtml(rawCode);
486 html += `<div class="code-wrapper"><div class="code-header"><span>${escapeHtml(lang)}</span><button class="copy-btn" onclick="copyCode(this)">${escapeHtml(UI_STRINGS.copy_label)}</button></div><pre data-raw="${ encodeURIComponent(rawCode) }"><code>${bodyHtml}</code></pre></div>`;
487 continue;
488 }
489 // Horizontal rule
490 if (/^---+$/.test(line.trim())) {
491 html += "<hr />";
492 i++;
493 continue;
494 }
495 // Headings
496 if (line.startsWith("# ")) {
497 html += `<h1>${formatInline(line.slice(2).trim())}</h1>`;
498 i++;
499 continue;
500 }
501 if (line.startsWith("## ")) {
502 html += `<h2>${formatInline(line.slice(3).trim())}</h2>`;
503 i++;
504 continue;
505 }
506 if (line.startsWith("### ")) {
507 html += `<h3>${formatInline(line.slice(4).trim())}</h3>`;
508 i++;
509 continue;
510 }
511 // Table
512 if (line.trim().startsWith("|") && i + 1 < lines.length && /^\|[\s:-]+\|/.test(lines[i+1].trim())) {
513 const headers = line.trim().slice(1, -1).split("|").map(c => formatInline(c.trim()));
514 i += 2;
515 const rows = [];
516 while (i < lines.length && lines[i].trim().startsWith("|")) {
517 rows.push(lines[i].trim().slice(1, -1).split("|").map(c => formatInline(c.trim())));
518 i++;
519 }
520 html += '<div class="table-wrap"><table><thead><tr>' + headers.map(h => `<th>${h}</th>`).join("") + '</tr></thead><tbody>' +
521 rows.map(r => '<tr>' + r.map(c => `<td>${c}</td>`).join("") + '</tr>').join("") + '</tbody></table></div>';
522 continue;
523 }
524 // Blockquote
525 if (line.startsWith(">")) {
526 const qLines = [];
527 while (i < lines.length && lines[i].startsWith(">")) {
528 qLines.push(lines[i].replace(/^>\s?/, ""));
529 i++;
530 }
531 html += `<blockquote><p>${qLines.map(formatInline).join("<br/>")}</p></blockquote>`;
532 continue;
533 }
534 // Unordered list
535 if (/^\s*-\s+/.test(line)) {
536 const items = [];
537 while (i < lines.length && /^\s*-\s+/.test(lines[i])) {
538 items.push(formatInline(lines[i].replace(/^\s*-\s+/, "")));
539 i++;
540 }
541 html += "<ul>" + items.map(it => `<li>${it}</li>`).join("") + "</ul>";
542 continue;
543 }
544 // Ordered list
545 if (/^\s*\d+\.\s+/.test(line)) {
546 const items = [];
547 while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) {
548 items.push(formatInline(lines[i].replace(/^\s*\d+\.\s+/, "")));
549 i++;
550 }
551 html += "<ol>" + items.map(it => `<li>${it}</li>`).join("") + "</ol>";
552 continue;
553 }
554 // Empty line
555 if (!line.trim()) {
556 i++;
557 continue;
558 }
559 // Paragraph
560 const pLines = [];
561 while (i < lines.length && lines[i].trim() && !lines[i].startsWith("#") && !lines[i].startsWith("```") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !/^---+$/.test(lines[i].trim()) && !/^\s*(-\s+|\d+\.\s+)/.test(lines[i])) {
562 pLines.push(formatInline(lines[i]));
563 i++;
564 }
565 html += `<p>${pLines.join(" ")}</p>`;
566 }
567 return html;
568 }
569
570 window.copyCode = function(btn) {
571 const pre = btn.closest(".code-wrapper").querySelector("pre");
572 const raw = decodeURIComponent(pre.getAttribute("data-raw"));
573 navigator.clipboard.writeText(raw);
574 const old = btn.textContent;
575 btn.textContent = UI_STRINGS.copied_label;
576 setTimeout(() => { btn.textContent = old; }, 1400);
577 };
578
579 function renderSidebar() {
580 const nav = document.getElementById("navList");
581 nav.innerHTML = CHAPTERS.map((ch, idx) => `
582 <li class="nav-item">
583 <button class="nav-btn ${idx === currentIdx ? 'active' : ''}" onclick="selectChapter(${idx})">
584 <span class="nav-num">${ch.num}</span>
585 <span>${escapeHtml(ch.short_title)}</span>
586 </button>
587 </li>
588 `).join("");
589 }
590
591 window.selectChapter = function(idx) {
592 currentIdx = idx;
593 const ch = CHAPTERS[idx];
594 location.hash = ch.slug;
595 renderSidebar();
596 let pagerHtml = '<div class="pager">';
597 if (idx > 0) {
598 pagerHtml += `<button class="pager-btn prev" onclick="selectChapter(${idx - 1})"><span class="pager-label">${UI_STRINGS.prev_label}</span><span class="pager-title">${escapeHtml(CHAPTERS[idx - 1].title)}</span></button>`;
599 } else {
600 pagerHtml += '<div></div>';
601 }
602 if (idx + 1 < CHAPTERS.length) {
603 pagerHtml += `<button class="pager-btn next" onclick="selectChapter(${idx + 1})"><span class="pager-label">${UI_STRINGS.next_label}</span><span class="pager-title">${escapeHtml(CHAPTERS[idx + 1].title)}</span></button>`;
604 }
605 pagerHtml += '</div>';
606 document.getElementById("chapterContent").innerHTML = renderMarkdown(ch.markdown) + pagerHtml;
607 window.scrollTo({ top: 0, behavior: "smooth" });
608 };
609
610 document.getElementById("searchInput").addEventListener("input", (e) => {
611 const q = e.target.value.trim().toLowerCase();
612 if (q.length < 2) {
613 selectChapter(currentIdx);
614 return;
615 }
616 const hits = [];
617 CHAPTERS.forEach((ch, idx) => {
618 const lines = ch.markdown.split("\n");
619 for (const l of lines) {
620 if (l.toLowerCase().includes(q)) {
621 hits.push({ idx, title: ch.title, snippet: l.replace(/^[#>*-]+\s*/, "").slice(0, 140) });
622 break;
623 }
624 }
625 });
626 const box = hits.length
627 ? `<div class="search-results"><strong>${escapeHtml(UI_STRINGS.search_hits_label)} "${escapeHtml(q)}" (${hits.length}):</strong>` +
628 hits.map(h => `<div class="search-hit" onclick="document.getElementById('searchInput').value=''; selectChapter(${h.idx})"><div class="search-hit-title">${escapeHtml(h.title)}</div><div class="search-hit-snippet">${escapeHtml(h.snippet)}</div></div>`).join("") +
629 `</div>`
630 : `<div class="search-results">${escapeHtml(UI_STRINGS.search_empty_label)} "${escapeHtml(q)}".</div>`;
631 document.getElementById("chapterContent").innerHTML = box + renderMarkdown(CHAPTERS[currentIdx].markdown);
632 });
633
634 document.getElementById("themeBtn").addEventListener("click", () => {
635 const root = document.documentElement;
636 root.setAttribute("data-theme", root.getAttribute("data-theme") === "dark" ? "light" : "dark");
637 });
638
639 // Initial load from URL hash if present
640 const initialSlug = location.hash.replace(/^#/, "");
641 const foundIdx = CHAPTERS.findIndex(c => c.slug === initialSlug);
642 selectChapter(foundIdx >= 0 ? foundIdx : 0);
643 </script>
644</body>
645</html>