Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | <!DOCTYPE html> |
| 2 | <html lang="gl" data-theme="dark"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8" /> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | <title>O Libro de Ridgun — Guía Completa da Linguaxe</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 active" href="index.html">GL</a><a class="lang-link" href="../eu/index.html">EU</a><a class="lang-link" href="../en/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>O Libro de Ridgun</h1> |
| 398 | <p>De cero a experto en programación funcional tabular e de sistemas</p> |
| 399 | </div> |
| 400 | <div class="search-box"> |
| 401 | <input type="search" id="searchInput" placeholder="Buscar no libro (ex. ||, Tensor, RMM2, TCO...)" /> |
| 402 | </div> |
| 403 | <ul class="nav-list" id="navList"></ul> |
| 404 | <div class="sidebar-footer"> |
| 405 | <span>9 capítulos (00–08) · Galego</span> |
| 406 | <button class="theme-btn" id="themeBtn" type="button">☀️ / 🌙 Tema</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-por-que-preciso-ridgun","file":"00-por-que-preciso-ridgun.md","title":"Capítulo 00: Por que preciso Ridgun? Ah, si? Non o sabía... Cóntame máis!","short_title":"Por que preciso Ridgun? Ah, si? Non o sabía... Cóntame máis!","markdown":"# Capítulo 00: Por que preciso Ridgun? Ah, si? Non o sabía... Cóntame máis!\n\nSexamos sinceros: cando alguén che di *\"creamos unha nova linguaxe de programación\"*, a túa primeira reacción natural —e moi sa— é erguer unha cella e pensar:\n\n> *\"Outra máis? Pero se xa teño Python para ir rápido, Rust ou C para cando preciso velocidade, e Bash para pegar cousas na terminal. Para que demo preciso eu aprender Ridgun?\"*\n\nÉ unha pregunta excelente. E a resposta curta é: **porque te acostumaches a sufrir catro dores diarias pensando que eran inevitables**.\n\nImos poñerlles nome.\n\n---\n\n## Dor n.º 1: \"A síndrome dos dous mundos\" (Prototipar nun, reescribir noutro)\n\nSeguro que xa viviches esta película:\n\n1. Comezas unha ferramenta ou un motor de cálculo en **Python**, **JavaScript** ou **Ruby** porque queres pensar no problema, non en punteiros nin no compilador.\n2. O código queda curto e bonito... pero cando lle pasas un ficheiro de 500 MB ou intentas usar os 16 núcleos da túa máquina, arrástrase. E se queres distribuír o teu programa a un servidor limpo, tes que empaquetar medio xigabyte de contorno virtual, intérprete e bibliotecas dinámicas.\n3. Resignado, decides reescribilo en **C**, **C++** ou **Rust**. Agora voa e ocupa uns poucos centos de kilobytes, pero o teu código de 40 liñas converteuse en 300 liñas cheas de `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, chaves `{}` e puntos e coma `;`.\n\n**Que muda con Ridgun?**\nEn Ridgun escribes código **máis curto e limpo ca en Python**, pero cando executas `ridgunc build`, obtés **un único binario nativo ELF de ~350 KB** sen intérprete, sen máquina virtual externa e sen colector de lixo. Non hai fase de *\"logo reescribímolo na linguaxe rápida\"*: o código limpo onde pensas o problema **xa é** o binario nativo de produción.\n\n---\n\n## Dor n.º 2: \"Ler código alleo (ou o teu de hai 6 meses) é xogar a ser detective\"\n\nMira como escribimos lóxica condicional en case todas as linguaxes convencionais:\n\n```text\nif (estado == ACTIVO && (rol == ADMIN || (rol == EDITOR && tenPermiso))) {\n if (cota > 0) {\n return procesar(usuario);\n } else {\n return denegar(\"Sen cota\");\n }\n} else {\n return denegar(\"Non autorizado\");\n}\n```\n\nOs nosos ollos non evolucionaron para emparellar chaves aniñadas nin para desentrañar árbores de `if / else` graduadas. Cando os enxeñeiros, matemáticos ou físicos queren entender regras complexas sen trabucarse, non escriben parágrafos de `if / else`: **debuxan unha táboa**.\n\nEn Ridgun, **o código é esa táboa**:\n\n```ridgun\nautorizar String String Bool Int => String\nautorizar \"activo\" \"admin\" _ cota =\n | cota > 0 = \"Procesando petición\"\n | otherwise = \"Denegado: sen cota\"\nautorizar \"activo\" \"editor\" True cota =\n | cota > 0 = \"Procesando petición\"\n | otherwise = \"Denegado: sen cota\"\nautorizar _ _ _ _ = \"Denegado: non autorizado\"\n```\n\nMira as columnas:\n- Columna 1: o estado (`\"activo\"` ou `_`).\n- Columna 2: o rol (`\"admin\"`, `\"editor\"` ou `_`).\n- Columna 3: se ten permiso (`True` ou `_`).\n- Columna 4: a `cota`.\n\nNon tes que executar mentalmente o programa liña por liña para saber que pasa cando entra un `\"editor\"` con permiso `False`: os teus ollos baixan verticalmente pola táboa e veno en medio segundo.\n\n---\n\n## Dor n.º 3: \"O meu ordenador ten 16 núcleos, pero o meu programa só usa 1 (porque me dá pánico tocar fíos)\"\n\nHoxe ata o teléfono máis modesto ten 8 núcleos, e calquera servidor ou portátil de traballo ten entre 12 e 32. Porén, o 95% dos programas cotiáns seguen executándose nun só fío secuencial.\n\nPor que? Porque nas linguaxes imperativas, paralelizar significa abrir a caixa de Pandora:\n- E se dous fíos modifican o mesmo dicionario á vez? (*Data race*).\n- E se bloqueo o `Mutex` A e logo o B nun fío, e o B e logo o A noutro? (*Deadlock*).\n- E se contamino toda a miña base de código con `async` e `await` de arriba abaixo?\n\nEn Ridgun, **toda función é pura e todo dato é inmutable por defecto**. Iso ten unha consecuencia máxica: **dúas expresións que non dependen unha da outra xamais poden interferir entre si**.\n\nPor iso, en Ridgun non tes que xestionar fíos para aproveitar a túa CPU. Só declaras **que depende de que** con barras paralelas `||` e frechas `->`:\n\n```ridgun\ninforme mensual Datos => Informe\ninforme mensual d = combinar metricas_rede metricas_disco metricas_cpu\n || analizar rede d -> metricas_rede\n || analizar disco d -> metricas_disco\n || analizar cpu d -> metricas_cpu\n```\n\nTi só escribiches tres liñas limpas indicando de onde sae cada dato. O compilador ve que as tres ramas son independentes e sabe que pode lanzalas en paralelo sen que exista nin a máis mínima posibilidade dun *data race* ou dun *deadlock*.\n\nE se precisas axentes persistentes con caixa de correo e estado illado (por exemplo, para manter miles de sesións de clientes, traballadores en segundo plano ou conexións de rede)? En Ridgun non tes que definir clases `Actor` nin usar `Mutex`: **calquera función normal convértese nun axente autónomo nunha soa liña con `agent`**:\n\n```ridgun\ncontador = agent (sumar visita) 0\nsend 1 to contador\nsend 5 to contador\ntotal = receive from contador\nstop contador\n```\n\nAdemais, o compilador realiza unha **elección intelixente en tempo de compilación**: analiza estaticamente a profundidade e o custo de cada función en `||`, `||map`, `||filter` e `agent` para executar as funcións lixeiras como pasos *green-thread* de sobrecarga cero e despachar automaticamente os cálculos pesados ou recursivos a fíos reais do kernel en múltiples núcleos (`SYS_clone`).\n\n---\n\n## Dor n.º 4: \"O colector de lixo decide pausar o mundo xusto no peor momento\"\n\nAs linguaxes funcionais clásicas (Haskell, OCaml, Erlang, Clojure) son marabillosas para razoar sobre o código, pero case todas dependen dun **Colector de Lixo (*Garbage Collector*)** en tempo de execución. Iso descártaas cando precisas:\n- Latencias predicibles ao microsegundo.\n- Controlar exactamente canta memoria RAM pode consumir unha operación antes de abortala limpamente.\n- Escribir utilidades do sistema operativo (`ls`, `find`, `sh`, `doas`, `make`) ou tocar rexistros de hardware (`MMIO`).\n\nRidgun dáche **toda a elegancia do mundo funcional puro, pero con Xestión de Memoria de Ridgun (`RMM`: `RMM1` estática e `RMM2` por conta de asignacións en fervenza) máis rexións léxicas con cota exacta (`with region`)**:\n\n```ridgun\nanalizar paquete String => Result\nanalizar paquete cru =\n with region 16_KB do\n decodificar trama cru\n```\n\nCada estrutura temporal é liberada de forma determinista polo compilador (`RMM1` / `RMM2`) no instante en que deixa de usarse, e se un atacante che envía un paquete malicioso deseñado para rebentar a memoria, ao superar o byte 16.384 a rexión córtase ao instante, libera a súa area en $O(1)$ e devólveche `Err OutOfMemory`. Sen caídas do proceso, sen pausas de GC e sen fugas de memoria.\n\n---\n\n## En resumo: Cando brilla Ridgun?\n\nRidgun está feito para ti se algunha vez quixeches:\n\n1. **Escribir ferramentas de sistemas, compiladores, analizadores ou utilidades CLI** que arrinquen en microsegundos, pesen uns poucos centos de KB e non poidan romper por punteiros nulos nin efectos colaterais ocultos.\n2. **Construír sistemas concorrentes e servidores baseados no Modelo Nativo de Axentes (`agent`, `send ... to`, `receive from`, `stop`)**, onde calquera función pura se converte nun axente con ou sen estado illado e o compilador escolle intelixentemente entre execución *green-thread* ultralixeira e fíos nativos multinúcleo.\n3. **Programar algoritmos, tubaxes de datos ou álxebra de tensores** onde o compilador verifique por ti ata as dimensións das matrices (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) antes de executar nada.\n4. **Gozar lendo o teu propio código**: que cada ficheiro `.rg` pareza unha páxina dun libro ben maquetado grazas ao formatador tabular automático (`stable format`).\n\nPícache a curiosidade? Pasa ao **Capítulo 01** e escribamos o noso primeiro programa en menos de dous minutos.\n"}, {"num":"01","slug":"01-benvido-a-ridgun","file":"01-benvido-a-ridgun.md","title":"Capítulo 01: O teu primeiro contacto con Ridgun (Sen cascos, sen chaves e sen puntos e coma)","short_title":"O teu primeiro contacto con Ridgun (Sen cascos, sen chaves e sen puntos e coma)","markdown":"# Capítulo 01: O teu primeiro contacto con Ridgun (Sen cascos, sen chaves e sen puntos e coma)\n\nVale, xa te convencín no Capítulo 00 de que non estamos tolos por crear Ridgun. Agora seguramente esteas pensando:\n\n> *\"Moi bonito o discurso das táboas e os 16 núcleos, pero ensíname código de verdade. Canto tarda un en escribir algo que funcione sen pelexar co compilador?\"*\n\nImos aló, mans á obra!\n\n---\n\n## 1.1. \"Ola, mundo!\", pero con substancia\n\nNa maioría dos libros de programación ensínanche un `print(\"Ola mundo\")` soso e pasan de páxina. Nós imos escribir directamente unha función con lóxica real para que vexas como respira Ridgun desde o minuto un.\n\nCrea un ficheiro chamado `ola.rg` e escribe isto:\n\n```ridgun\nsaudo String => String\nsaudo \"\" = \"Ola, viaxeiro misterioso! (Non me dixeches o teu nome)\"\nsaudo \"Root\" = \"Home, Root! Coidado co que borras hoxe.\"\nsaudo nome = \"Ola, {nome}! Benvido a Ridgun.\"\n\nmain => ()\nmain =\n echo (saudo \"Ada\")\n echo (saudo \"Root\")\n echo (saudo \"\")\n```\n\nPara executalo agora mesmo:\n\n```bash\nridgunc run ola.rg\n```\n\nE verás na túa terminal:\n\n```text\nOla, Ada! Benvido a Ridgun.\nHome, Root! Coidado co que borras hoxe.\nOla, viaxeiro misterioso! (Non me dixeches o teu nome)\n```\n\nPara un momento e mira o bloque de `saudo` arriba. Notas o que **NON** hai?\n- **Non hai `function`, nin `def`, nin `pub fn`** (se queres poñer `fn ::` diante da sinatura podes facelo, pero é opcional).\n- **Non hai parénteses `(String nome)` nin chaves `{ ... }`** lixando a pantalla.\n- **Non hai `if (nome == \"\") ... else if (nome == \"Root\") ... else ...`**.\n- **Non hai `return`**.\n\n---\n\n## 1.2. Como le o teu cerebro un ficheiro `.rg`\n\nImos destripar esas catro liñas de `saudo`, porque aquí xa está o 50% da filosofía de Ridgun:\n\n```ridgun\nsaudo String => String\nsaudo \"\" = \"Ola, viaxeiro misterioso! (Non me dixeches o teu nome)\"\nsaudo \"Root\" = \"Home, Root! Coidado co que borras hoxe.\"\nsaudo nome = \"Ola, {nome}! Benvido a Ridgun.\"\n```\n\n1. **A primeira liña é o contrato (`saudo String => String`)**:\n Di: *\"Chámome `saudo`, recibo un `String` e devólvoche (`=>`) outro `String`\"*. Punto. Se alguén intenta chamarme pasándome o número `42`, o compilador daralle un toque no ombreiro antes sequera de executar o programa.\n2. **As liñas de abaixo son os casos (de arriba abaixo)**:\n - Pasáchesme a cadea baleira `\"\"`? Devolvo a primeira frase.\n - Pasáchesme exactamente `\"Root\"`? Devolvo a segunda frase.\n - Pasáchesme calquera outra cousa? Gárdoa na variable `nome` e métoa dentro do texto usando `{nome}`.\n3. **E que é `main => ()`?**:\n Como `main` non recibe argumentos, poñemos `=>` directamente despois do nome. E devolve `()` (o tipo baleiro ou *Unit*, para os amigos), porque o seu único traballo é sacar cousas por pantalla con `echo`.\n\n---\n\n## 1.3. \"Agarda... teño que aliñar eses espazos á man coma un monxe copista?\"\n\nNin de broma! Se tiveses que darlle á barra espazadora contando columnas a ollo, abandonarías Ridgun aos dez minutos (e con razón).\n\nTi escribes o código axiña, cun só espazo entre cada palabra:\n\n```ridgun\nsaudo String => String\nsaudo \"\" = \"Ola, viaxeiro misterioso!\"\nsaudo nome = \"Ola, {nome}!\"\n```\n\nE cando executas **`stable format`** (ou premes `TAB` / gardas o ficheiro en Emacs con `ridgun-mode`), o formatador canónico coloca cada parámetro debaixo do seu tipo e aliña todos os `=` con precisión milimétrica. Ti escribes rápido; a máquina maqueta a táboa.\n\n---\n\n## 1.4. De script rápido a binario de combate nun comando\n\nCando fas `ridgunc run ola.rg`, estás compilando e lanzando o programa ao voo. Pero imaxina que xa remataches a túa ferramenta e queres levala a un servidor de produción ou compartila cun compañeiro que non ten instalado Ridgun.\n\nExecutas:\n\n```bash\nridgunc build ola.rg -o ola\n```\n\nE xa tes `./ola`: un **executable nativo ELF de Linux x86_64**, limpo, sen código morto (*Tree-Shaking*), con optimización de ligazón completa (*LTO*) e sen depender de ningún intérprete externo.\n\n---\n\n## 1.5. A chuleta honesta: De onde vén cada idea de Ridgun?\n\nRidgun non pretende reinventar a roda cadrada; toma prestado sen complexos o mellor de catro tradicións:\n\n| De quen | Que toma Ridgun | Que deixa fóra (pola túa saúde mental) |\n|---|---|---|\n| **Haskell / ML** | Funcións puras, *pattern matching* por casos, tipos alxébricos (`Option`, `Result`) e polimorfismo (`A`..`Z`). | As mónadas abstractas, os 40 `LANGUAGE pragmas` e o colector de lixo. |\n| **J / APL** | A obsesión pola forma tabular do código, tensores nativos e máscaras de secuencias (`s(_ + _)`). | Os símbolos xeroglíficos imposibles de escribir nun teclado normal. |\n| **Rust / C** | Binarios ELF nativos pequenos, cero GC, control de memoria (`with region`) e chamadas ao sistema POSIX. | A pelexa constante co *borrow checker*, os *lifetimes* `'a` e a verbosidade sintáctica. |\n| **Erlang / Go** | Axentes illados (`agent`, `send ... to`, `receive from`) e concorrencia por canles (`new bounded channel`). | Os punteiros `nil` e as mutacións compartidas por descoido. |\n\n---\n\n## 1.6. E o máis importante: Que fai Ridgun que NON ten ninguén máis?\n\nRecoñecer os clásicos é de boa educación, pero se Ridgun fose só un \"compilatorio de grandes éxitos\" doutras linguaxes, non estarías lendo este libro.\n\nHai **oito cousas** que deseñamos e integramos en Ridgun que non vas atopar xuntas —e moitas delas, nin sequera por separado— en ningunha outra linguaxe actual:\n\n### 1. Bloques paralelos `||` con `->` que son grafos paralelos reais, non simple azucre sintáctico\nNoutras linguaxes funcionais un bloque de variables locais só ordena expresións que logo se avalían nun único fío.\nEn Ridgun, **un bloque `||` con frechas `->` é un Grafo Acíclico Dirixido (DAG) de execución paralela** sen sequera precisar escribir palabras chave extra como `where`:\n```ridgun\ninforme anual Int => Int\ninforme anual id = ingresos - gastos\n || calcular vendas id -> ingresos\n || calcular custos id -> gastos\n```\nTi debuxas as frechas de fluxo (`->`). O compilador analiza quen depende de quen, **verifica estaticamente que non crearas ningún ciclo infinito**, e lanza as ramas independentes en paralelo sen que escribas nin un só `async`, `await`, `Future` nin `Mutex`.\n\n### 2. Modelo Nativo de Axentes (`agent`) de cero fricción con elección intelixente en compilación\nNoutras linguaxes, programar con actores ou axentes obrígate a crear clases especiais, implementar *traits*, xestionar *locks* ou infectar as túas funcións con `async`.\nEn Ridgun, **calquera función pura normal (`fn ::` ou `op ::`) convértese directamente nun axente autónomo —con ou sen estado illado— usando `agent`**:\n```ridgun\natender http (Sesion, String) => (Sesion, String)\n...\nservidor = agent atender http sesion_inicial\nsend \"GET /perfil\" to servidor\nresposta = receive from servidor\nstop servidor\n```\nMellor aínda: o compilador **analiza en tempo de compilación a profundidade e o custo computacional da función** tanto en `agent` coma en `||` e `||map`. Se a función é lixeira, execútaa como un *green thread* instantáneo sen pagar o custo de quentar fíos do sistema operativo; se é un cálculo pesado ou recursivo profundo, despáchao automaticamente en paralelo sobre fíos reais do kernel (`SYS_clone`) repartidos polos núcleos da túa CPU.\n\n### 3. O código como táboa bidimensional onde a sinatura é a cabeceira das columnas\nEn calquera outra linguaxe, a sinatura de tipos vai por libre nunha liña e o corpo da función vai debaixo como un parágrafo de texto normal.\nEn Ridgun, **a sinatura de tipos é literalmente a cabeceira dunha táboa**, e cada caso debaixo aliña cada patrón xusto na vertical do seu tipo e cada `=` baixo o `=>`:\n```ridgun\ndividir Int Int => Option\ndividir _ 0 = None\ndividir a b = Some (a / b)\n```\nNinguén máis trata a sinatura e as ecuacións como unha única matriz visual verificada polo compilador e maquetada automaticamente por `stable format`.\n\n### 4. Operadores frase (*Mixfix* N-arios) tipados e aliñados (`op ::`)\nNa maioría das linguaxes, ou te aguantas con `+`, `-`, `*` ou como moito defines un operador binario críptico como `<|>` que ninguén sabe que significa.\nEn Ridgun podes crear **operadores que son frases lexibles con tantos ocos tipados como queiras** (incluíndo palabras contextuais como `type` ou `with`):\n```ridgun\nop :: Int limitado entre Int e Int => Int\n x limitado entre min e max =\n | x < min = min\n | x > max = max\n | otherwise = x\n```\nE logo no teu código escribes `velocidade limitado entre 0 e 120`. Lese coma galego (ou inglés), pero está 100% tipado e compilado a código nativo.\n\n### 5. Máscaras visuais sobre secuencias (`s(_ + _)` e `-> (+ _ _ +) ->`)\nAlgunha vez tiveches que escribir unha ensalada de índices ou abrir unha tupla con variables mudas só para quedar con dúas posicións concretas?\nEn Ridgun pásaslle directamente unha **máscara visual**:\n- `datos(_ + _)`: descarta o primeiro (`_`), queda co segundo (`+`), descarta o terceiro (`_`).\n- `parsear paquete \"v0.9.9\" -> (+ _ _ +) -> combinar codigo -> resumo`: filtra unha tupla de 4 elementos no medio dunha tubaxe e pasa o 1º e o 4º directamente como argumentos da seguinte función.\nCero aritmética de índices, cero erros *off-by-one*.\n\n### 6. Polimorfismo de \"cero ruído\" (`A`..`Z`) gobernado polo teu dominio\nEn Rust, TypeScript ou Java, cando unha función é xenérica tes que encher a liña de parénteses angulares: `fn intercambiar<A, B>(par: (A, B)) -> (B, A)`.\nEn Ridgun simplemente usas unha letra maiúscula solta:\n```ridgun\nintercambiar (A, B) => (B, A)\nintercambiar (x, y) = (y, x)\n```\n*\"E se no meu programa quero ter un tipo real que se chame `type A`?\"* — Ningún problema: en Ridgun **as túas declaracións `type` mandan**. Se non existe `type A`, `A` é xenérico; no instante en que defines `type A = ...`, pasa a ser o teu tipo concreto en todo o módulo.\n\n### 7. Cero ficheiros de configuración estraños: `build.rg` é Ridgun puro\nEsquece aprender TOML para Cargo, JSON para npm, XML para Maven ou a sintaxe arcana de `Makefile` con tabuladores invisibles. En Ridgun, a receita que constrúe o teu proxecto (`build.rg`) **é un ficheiro `.rg` máis**, coas mesmas funcións puras e o mesmo *pattern matching* có resto do teu código.\n\n### 8. A \"Santísima Trindade\" que parecía imposible: Nivel Haskell + Memoria RMM sen GC nin Lifetimes + Binarios de ~350 KB\nAta hoxe tiñas que escoller:\n- Se querías expresividade funcional pura (Haskell, OCaml, Elixir), pagabas cun colector de lixo e un runtime pesado.\n- Se querías binarios diminutos de sistema sen colector de lixo (C, Rust, Zig), pagabas xestionando punteiros ou pelexando co *borrow checker* e escribindo código imperativo cheo de `let mut`.\n\nRidgun rompe ese dilema combinando **Ridgun Memory Management (`RMM`: `RMM1` estática e `RMM2` por conta de asignacións en fervenza)**, **rexións léxicas (`with region`)** que liberan areas enteiras en $O(1)$, **recursión de cola transformada en bucles nativos (`loop`)**, e **poda de grafo de chamadas (*Tree-Shaking*)** desde `main`. O resultado: escribes código declarativo de altísimo nivel e obtés un executable ELF autónomo de **~350 KB** capaz de competir de ti a ti con ferramentas escritas en C.\n\nAgora si: coas cartas sobre a mesa, imos ao **Capítulo 02** para poñer a traballar a **`ridgunc`** e **`stable`**.\n"}, {"num":"02","slug":"02-primeiros-pasos-e-ferramentas","file":"02-primeiros-pasos-e-ferramentas.md","title":"Capítulo 02: Os teus dous mellores amigos: `ridgunc` e `stable` (Cero ficheiros YAML, prometido)","short_title":"Os teus dous mellores amigos: `ridgunc` e `stable` (Cero ficheiros YAML, prometido)","markdown":"# Capítulo 02: Os teus dous mellores amigos: `ridgunc` e `stable` (Cero ficheiros YAML, prometido)\n\nHai linguaxes cuxo compilador é marabilloso, pero cuxo sistema de proxectos parece deseñado polo teu peor inimigo: que se un `Makefile` de 400 liñas con tabuladores invisibles, que se un `pom.xml` infinito, que se un `webpack.config.js` que deixa de funcionar se o miras fixo...\n\nEn Ridgun fixémonos unha promesa moi simple desde o primeiro día:\n\n> *\"Só haberá dous executables en todo o ecosistema (`ridgunc` e `stable`), ambos estarán escritos no propio Ridgun, e o ficheiro de configuración do proxecto (`build.rg`) será código Ridgun normal e corrente. Nin JSON, nin YAML, nin TOML, nin inventos raros.\"*\n\nImos ver como se usan.\n\n---\n\n## 2.1. `ridgunc`: O francotirador para ficheiros soltos\n\nCando tes un único ficheiro `.rg` entre mans e queres probar unha idea rápida, analizar os seus tipos ou compilar un executable, falas directamente con **`ridgunc`**.\n\nSó ten 4 comandos que de verdade precisas lembrar:\n\n```bash\n# 1. \"Compila isto ou metín a pata con algún tipo?\"\nridgunc check experimento.rg\n\n# 2. \"Execútao xa!\" (Compila e corre dun tirón)\nridgunc run experimento.rg\n\n# 3. \"Faime un binario nativo ELF listo para levar\"\nridgunc build experimento.rg -o meu_programa\n\n# 4. \"Sácame o artefacto intermedio de bytecode (.rgc)\"\nridgunc compile experimento.rg -o experimento.rgc\n```\n\n### O superpoder oculto de `ridgunc check`\nCando lanzas `ridgunc check`, non só che di *\"0 erros\"*. Tamén che sopra que estratexia escolleu o analizador estático para cada unha das túas funcións:\n- **`InlineSequential`**: *\"Esta función é pequeniña e directa; vouna executar en liña sen meter sobrecusto de planificación\"*.\n- **`LazyStealableTask`**: *\"Aquí hai recursión ou un grafo paralelo `||`; prepároa para que outros núcleos de CPU poidan roubar traballo se fai falta\"*.\n\n### O bisturí de `ridgunc build` (Por que os teus binarios non pesan 20 MB)\nCando executas `ridgunc build`, o compilador non se limita a cuspir todo o código das bibliotecas que importaras:\n1. Comeza na túa función `main` e percorre o grafo de chamadas para ver **que funcións usas de verdade**. Se importaches unha biblioteca enorme de 80 funcións pero só chamas a 2, as outras 78 descártanse antes sequera de tocar o ensamblador (*Tree-Shaking*).\n2. Busca todas as funcións recursivas de cola e convérteas en bucles `loop` nativos que consumen **0 bytes de pila**.\n3. Pasa a ligazón final con optimización global (*fat LTO*) e limpa o lixo de depuración DWARF para deixarche un binario compacto e afiado (~350 KB).\n\n---\n\n## 2.2. `stable`: Cando o teu proxecto medra e queres orde\n\nUn ficheiro solto está xenial para un script, pero en canto tes varios módulos, bibliotecas compartidas e probas, precisas un director de orquestra. Ese é **`stable`**.\n\nA estrutura de cartafoles dun proxecto Ridgun non ten misterio:\n\n```text\na-mina-super-ferramenta/\n├── build.rg # O manifesto do proxecto (é código Ridgun!)\n├── src/\n│ ├── main/\n│ │ └── ridgun/\n│ │ ├── lib/ # Os teus módulos reutilizables (`import utilidades`)\n│ │ └── app/\n│ │ └── main.rg # Punto de entrada (`main => ()`)\n│ └── test/\n│ └── ridgun/ # As túas baterías de probas (`test *.rg`)\n│ └── test todo.rg\n└── target/ # Onde aparecen os binarios compilados\n```\n\n---\n\n## 2.3. `build.rg`: Un ficheiro de configuración que nunca che mentirá\n\nPor que nos negamos a usar YAML ou TOML para configurar un proxecto Ridgun?\nPorque en YAML trabúcaste en dous espazos de sangrado e non te decatas ata que falla o despregamento. En cambio, como **`build.rg` é un ficheiro Ridgun**, ten tipos estáticos, pasa por `stable check` e alíñase só con `stable format`:\n\n```ridgun\ntype ProjectInfo =\n name: String\n version: String\n edition: String\n\nproject => ProjectInfo\nproject = ProjectInfo \"a-mina-super-ferramenta\" \"0.1.0\" \"2026\"\n\nsource dir => String\nsource dir = \"src/main/ridgun\"\n\ntest dir => String\ntest dir = \"src/test/ridgun\"\n\ntarget dir => String\ntarget dir = \"target\"\n\n-- Opcional: activa o modo estrito de sinaturas e nomes (desactivado por defecto)\nstrict mode => Bool\nstrict mode = True\n```\n\nSe te trabucas ao escribir un campo, o propio compilador avísate. Sen sorpresas.\n\n### O modo estrito (`--strict` / `strict mode = True`)\nPor defecto, tanto `ridgunc` como os novos proxectos creados con `stable new` ou `stable init` teñen o modo estrito **desactivado** para que poidas prototipar con total liberdade. Cando queiras blindar o estilo canónico dun proxecto (como facemos no propio compilador de Ridgun), podes pasar `--strict` a `ridgunc` ou engadir `strict mode = True` ao teu `build.rg`. O modo estrito esixe dúas regras de elegancia:\n1. **Nomes de `fn ::` e `op ::` con espazos naturais, sen `_`**: as declaracións deben usar espazos (`fn :: calcular imposto`, nunca `fn :: calcular_imposto`).\n2. **Retornos múltiples separados por espazos tras `=>`**: cando unha función ou operador devolve varios valores, a súa sinatura de saída escríbese `=> Int Int` ou `=> Estado Int` (nunca `=> (Int, Int)` con parénteses nin comas).\n\n---\n\n## 2.4. A túa rutina diaria con `stable` (Os 4 fantásticos)\n\nCando estás traballando dentro do teu proxecto, o teu día a día redúcese a catro comandos:\n\n### 1. `stable format` — A fin das discusións de estilo no equipo\n```bash\nstable format\n```\nQue o teu compañeiro pon tres espazos antes do `=>` e ti pos un? Dá igual. Executas `stable format` e en menos dun segundo deixa **todos** os ficheiros `.rg` do repositorio aliñados coma unha táboa suíza.\n\n### 2. `stable check` — O radar instantáneo\n```bash\nstable check\n```\nPercorre todos os módulos de `src/main/ridgun/`, verifica tipos, construtores, aridades e dimensións de tensores, e dáche o veredicto módulo por módulo.\n\n### 3. `stable test` — Durmir tranquilo polas noites\n```bash\nstable test\n```\nBusca todas as suites `src/test/ridgun/test *.rg`, execútaas e comproba cada `assert equals` e `assert true`.\n\n```ridgun\n-- Exemplo de src/test/ridgun/test mates.rg\nimport test\n\ndobro Int => Int\ndobro x = x * 2\n\nmain => ()\nmain =\n assert equals (dobro 21) 42\n assert true ((dobro 5) > 0)\n```\n\n### 4. `stable built` (e `stable clean`) — A produción\n```bash\nstable built\n```\nCompila todos os obxectivos nativos en `target/`. E se algún día queres borrar `target/` e comezar limpo: `stable clean`.\n\n---\n\n## 2.5. Como importo un ficheiro desde outro? (`import`)\n\nSen rutas relativas infernais tipo `../../../lib/cousas.rg`. Se tes un ficheiro `src/main/ridgun/lib/cadeas.rg`, en calquera outro ficheiro escribes arriba de todo:\n\n```ridgun\nimport cadeas\n```\n\nE listo: todas as funcións e tipos de `cadeas.rg` están dispoñibles coma se estivesen no teu propio ficheiro. E que pasa se o módulo `A` fai `import cadeas`, o módulo `B` tamén fai `import cadeas`, e o teu `main.rg` importa `A` e `B`? Tranquilo: o cargador de Ridgun lembra que ficheiros canónicos leu xa e **só o inclúe unha vez**.\n\nAgora que tes a caixa de ferramentas controlada, pasemos ao **Capítulo 03** para dominar as funcións, os tipos, os bloques e os trucos con cadeas de texto.\n"}, {"num":"03","slug":"03-funcions-tipos-e-alinamento","file":"03-funcions-tipos-e-alinamento.md","title":"Capítulo 03: Funcións, sinaturas de tipos e a arte de escribir código coma unha táboa","short_title":"Funcións, sinaturas de tipos e a arte de escribir código coma unha táboa","markdown":"# Capítulo 03: Funcións, sinaturas de tipos e a arte de escribir código coma unha táboa\n\nHai unha pregunta que todo programador se fai a primeira vez que ve código funcional:\n\n> *\"Se aquí non hai clases, nin obxectos, nin variables que mudan de valor... entón onde meto a miña lóxica?\"*\n\nEn **funcións**. Pero non nesas funcións de 200 liñas cheas de bandeiras booleanas que todos sufrimos algunha vez, senón en funcións pequenas, directas e tan fáciles de ler que parecen unha táboa nun encerado.\n\nImos ver como se constrúen.\n\n---\n\n## 3.1. Dúas formas de presentar unha función (Ti escolles o traxe)\n\nEn Ridgun toda función comeza coa súa **sinatura de tipos** (que entra e que sae tras `=>`) e debaixo van as súas **cláusulas de implementación** (con `=`).\n\nPodes escribir a cabeceira de dúas maneiras:\n\n### Opción A: Directa ao gran (sen prefixo)\n```ridgun\naplicar desconto Int Int => Int\naplicar desconto prezo porcentaxe = prezo - (prezo * porcentaxe / 100)\n```\n\n### Opción B: Con etiqueta explícita (`fn ::`)\n```ridgun\nfn :: aplicar desconto Int Int => Int\n aplicar desconto prezo porcentaxe = prezo - (prezo * porcentaxe / 100)\n```\n\n*\"Ouve, e por que existen as dúas?\"*\nPorque para gustos, as cores:\n- Hai quen prefire que as funcións comecen pegadas á marxe esquerda sen escribir `fn ::` cada vez (ao fin e ao cabo, a frecha `=>` xa berra aos catro ventos que esa liña é unha sinatura de tipos).\n- E hai quen prefire ter unha columna esquerda dedicada ás etiquetas (`fn ::`, `op ::`, `type`, `import`) para escanear o ficheiro visualmente.\n\nO compilador e `stable format` entenden ambas á perfección. Ademais, as palabras `fn` e `op` poden usarse libremente como parte do nome de calquera función ou operador (por exemplo `fn :: op`, `op :: fn` ou `op :: op`), e se unha función devolve múltiples valores, en modo canónico/estrito os tipos de saída escríbense separados por espazos tras `=>` (`dividir con resto Int Int => Int Int`).\n\n---\n\n## 3.2. A maxia das columnas: por que os teus ollos descansan\n\nMira esta función que calcula o custo dun envío segundo o país e se é urxente:\n\n```ridgun\ncusto envio String Bool Int => Int\ncusto envio \"ES\" False _ = 5\ncusto envio \"ES\" True _ = 12\ncusto envio _ False peso = 10 + (peso * 2)\ncusto envio _ True peso = 25 + (peso * 4)\n```\n\nFai a proba: tapa a metade dereita da pantalla e mira só as tres columnas de entrada:\n- Primeira columna (`String`): `\"ES\"`, `\"ES\"`, `_`, `_`.\n- Segunda columna (`Bool`): `False`, `True`, `False`, `True`.\n- Terceira columna (`Int`): `_`, `_`, `peso`, `peso`.\n\nVes algún burato lóxico? Ningún: están as 4 combinacións posibles diante dos teus fociños, aliñadas verticalmente debaixo de `String`, `Bool` e `Int`. Nun bloque de `if / else` tradicional terías que ler 18 liñas para estar seguro de que non deixaches ningún caso solto.\n\n---\n\n## 3.3. Os tipos básicos e un agasallo para non contar ceros (`_KB`, `_MB`, `_GB`)\n\nOs tipos escalares de Ridgun son directos e sen sorpresas:\n\n| Tipo | Que é en realidade | Exemplos |\n|---|---|---|\n| `Int` | Enteiro de 64 bits con signo (`i64`) | `0`, `42`, `-100`, `0xFF00`, `1_000_000` |\n| `U64` | Enteiro de 64 bits sen signo (`u64`) con aritmética modular e bit a bit nativa | `u64 0`, `u64 0x6a09e667f3bcc908`, `a ^ b`, `a << u64 13` |\n| `Float` / `Real` | Decimal de dobre precisión (`f64`) | `3.14159`, `0.001`, `-2.5` |\n| `String` | Cadea de texto inmutable | `\"Ola\"`, `'ruta\\crua'` |\n| `Char` | Código de carácter | Devolto por `str char at` |\n| `Bool` | Booleano de toda a vida | `True`, `False` |\n| `()` | Baleiro (*Unit*, cando só imprimes ou fas E/S) | `()` |\n\n### Canso de escribir `65536` ou `1048576` e rezar para non comer un díxito?\nEn Ridgun os literais numéricos aceptan directamente os sufixos **`_KB`**, **`_MB`** e **`_GB`**, ademais de guións baixos `_` para separar grupos de cifras:\n\n```ridgun\ntamano buffer => Int\ntamano buffer = 64_KB -- O compilador tradúceo a 65536\n\nlimite ficheiro => Int\nlimite ficheiro = 16_MB -- O compilador tradúceo a 16777216\n\nmascara octeto => Int\nmascara octeto = 0xFF_00 -- Hexadecimal limpo e lexible\n```\n\n---\n\n## 3.4. Comiñas dobres (`\"...\"`) vs. Comiñas simples (`'...'`)\n\nEn moitas linguaxes as comiñas simples e dobres fan exactamente o mesmo (un desperdicio). En Ridgun cada unha ten unha misión clarísima:\n\n### 1. Comiñas dobres (`\"...\"`): Intelixentes e interpoladas\nInterpretan escapes (`\\n`, `\\t`, `\\\"`, `\\\\`) e **avalían calquera variable ou expresión que metas entre chaves `{...}`**:\n\n```ridgun\nresumo pedido String Int Int => String\nresumo pedido item uds prezo =\n \"Pediches {uds}x {item}. Total a pagar: {uds * prezo} euros.\"\n```\n\n*(Truco: se precisas escribir unha chave literal dentro de comiñas dobres —por exemplo ao xerar un script de shell `\"$\\{VAR:-default\\}\"` ou un JSON—, podes escapala con barra invertida `\\{` e `\\}` ou duplicala `{{` e `}}`).*\n\n### 2. Comiñas simples (`'...'`): Crúas e literais (*Raw Strings*)\nO que escribes entre comiñas simples sae **tal cal**, sen tocar nin unha barra invertida `\\`, nin unha chave `{}`. Perfecto para cando xeras código noutra linguaxe, escribes comandos de shell ou traballas con rutas cheas de barras:\n\n```ridgun\ncomando sed => String\ncomando sed = 's/\\([0-9]+\\)/[\\1]/g'\n```\n\n### A túa caixa de ferramentas para cadeas (`String`)\nUnir cadeas é tan simple como sumalas con `+`: `\"Ola, \" + nome`. E cando precisas facer cirurxía fina sobre un texto, tes estas funcións nativas listas sen importar nada:\n\n- `str len s` $\\rightarrow$ lonxitude de `s`.\n- `str char at s i` $\\rightarrow$ código numérico (`Int`) do carácter na posición `i` (`-1` se saes fóra).\n- `str slice s inicio fin` $\\rightarrow$ recorta desde `inicio` ata `fin` (sen incluír `fin`).\n- `char from code c` $\\rightarrow$ converte un código numérico (`Int`, p. ex. `10` para `\\n` ou `34` para `\"`) nun `String` de 1 carácter.\n- `str contains s agulla` e `str starts with s prefixo` $\\rightarrow$ devolven `True` ou `False`.\n- `int to str n` e `str to int s` $\\rightarrow$ converten de número a texto e viceversa (`str to int` entende mesmo `\"0xFF\"`!).\n\n---\n\n## 3.5. Bloques de varias liñas, a regra universal do `=` e variables `mut`\n\nCando unha función non cabe nunha soa liña, fas un salto de liña despois do `=` e sangras o bloque.\n\nMira como se le un bloque paso a paso:\n\n```ridgun\ncalcular factura Int Int => Int\ncalcular factura base ive_pct =\n impostos = base * ive_pct / 100\n gastos_xestion = 5\n echo \"Desagregación -> Base:\" base \"Impostos:\" impostos\n base + impostos + gastos_xestion\n```\n\nCatro regras de ouro que fan que os bloques en Ridgun sexan limpos e uniformes:\n1. **Inmutable por defecto sen `let` nin `const`**: escribes `impostos = ...` e xa tes a túa constante local inmutable. Se por descoido intentas sobrescribir `impostos = 99` máis abaixo no mesmo bloque, o compilador fréate en seco.\n2. **Variables mutables explícitas con `mut`**: cando de verdade queres ir actualizando un acumulador local paso a paso dentro dunha función, decláralo con **`mut`** e logo reasígnalo libremente con `=`:\n ```ridgun\n aplicar recargos Int => Int\n aplicar recargos prezo =\n mut total = prezo\n total = total + 10\n total = total * 2\n total\n ```\n *(E se `total` garda unha lista ou estrutura en memoria, a política **`RMM2`** de Ridgun suma `+1` ao novo valor antes de descontar `-1` ao antigo, liberándoo ao instante se ninguén máis o usa).*\n3. **A regra universal do `=`**: detrás de **calquera `=`** (nunha función, nun operador, nunha variable local `x =` ou nunha rama condicional) podes poñer sempre calquera das tres formas de bloque de Ridgun:\n - **`(nada)`**: liñas secuenciais sangradas (como arriba).\n - **`|`**: unha árbore de gardas condicionais.\n - **`||`**: un grafo de cálculos en paralelo con `->`.\n4. **A última expresión é sempre o resultado**: `base + impostos + gastos_xestion` é o que devolve `calcular factura`. Sen `return`.\n\nNo **Capítulo 04** imos ver como tomar decisións complexas —incluíndo árbores de decisión de varios niveis con `|.`— sen escribir xamais un `if/else` nin un bucle `while`: benvido ao mundo das **gardas booleanas** e a **recursión de cola de custo cero**.\n"}, {"num":"04","slug":"04-pattern-matching-e-gardas","file":"04-pattern-matching-e-gardas.md","title":"Capítulo 04: Adeus ao `if/else`, adeus ao `while` (Pattern Matching, Gardas e TCO)","short_title":"Adeus ao `if/else`, adeus ao `while` (Pattern Matching, Gardas e TCO)","markdown":"# Capítulo 04: Adeus ao `if/else`, adeus ao `while` (Pattern Matching, Gardas e TCO)\n\nCando lle contas a alguén que en Ridgun **non existe a palabra chave `if`, nin `else`, nin `switch`, nin `for`, nin `while`**, adoita mirarte coma se lle dixeses que vas construír unha casa sen martelo nin cravos:\n\n> *\"Que non hai `if` nin `while`? E como demo tomo unha decisión ou percorro un millón de datos?\"*\n\nPois resulta que `if/else` e `while` son ferramentas bastante toscas herdadas de como funcionaban os saltos (`GOTO`) nos anos 50. En Ridgun substituímolos por tres pezas que encaixan coma unha luva:\n1. ***Pattern Matching*** (cando decides segundo a **forma ou valor exacto** do dato).\n2. **Gardas booleanas `|`** (cando decides segundo unha **condición ou rango**).\n3. **Recursión de cola con TCO** (que o compilador converte por baixo nun bucle `loop` nativo ultrarrápido).\n\nImos velas en acción.\n\n---\n\n## 4.1. *Pattern Matching*: Cando o argumento xa che dá a resposta\n\nNo capítulo anterior xa vimos un adianto. En lugar de recibir unha variable xenérica `x` e abrir dentro da función cinco `if (x == ...)`, en Ridgun pos os casos directamente na porta de entrada da función:\n\n```ridgun\ncodigo http Int => String\ncodigo http 200 = \"OK: Todo foi sobre rodas\"\ncodigo http 301 = \"Movido permanentemente\"\ncodigo http 404 = \"Non atopado (miraches debaixo do sofá?)\"\ncodigo http 500 = \"Lume no servidor\"\ncodigo http n = \"Código descoñecido: {n}\"\n```\n\nRidgun proba as filas **de arriba abaixo**:\n- Se entra `404`, salta directo á terceira fila.\n- Se entra `418`, ningunha das catro primeiras filas coincide, así que cae na última (`n`), que atrapa calquera enteiro e o nomea `n` para que poidas imprimilo.\n- E se no último caso nin sequera che importa que número era? Pos un guión baixo `_` (*comodín*) e esquéceste: `codigo http _ = \"Outro código\"`.\n\n---\n\n## 4.2. Gardas booleanas (`|`): O teu bisturí para rangos, variables condicionais e árbores de decisión\n\n*\"Vale, o pattern matching mola cando busco o `200` ou o `404`. Pero que fago se quero comprobar se `nota >= 5` ou se `idade > 18 and idade < 65`? Non vou escribir 47 filas con todos os números do 18 ao 65!\"*\n\nClaro que non! Para iso están as **gardas (`|`)**.\n\nCando o resultado depende dunha desigualdade ou dunha combinación lóxica, despois do `=` da función (ou de calquera variable local) abres unha escaleira de barras verticais `|`:\n\n```ridgun\ncualificar Int => String\ncualificar nota =\n | nota < 0 = \"Nota imposible (puntos negativos?)\"\n | nota < 5 = \"Suspenso\"\n | nota < 7 = \"Aprobado\"\n | nota < 9 = \"Notable\"\n | nota <= 10 = \"Sobresaliente\"\n | otherwise = \"Pasácheste da escala\"\n```\n\nMírao ben:\n- O signo `=` da cabeceira (`cualificar nota =`) alíñase debaixo de `=>` igual ca en calquera outra función.\n- Debaixo lese de arriba abaixo igual ca unha táboa de tarifas: cada liña `| condicion = resultado` compróbase en orde. En canto unha dá `True`, devolve ese resultado e rematou.\n- Ao final pos **`| otherwise = ...`**, que significa literalmente *\"en calquera outro caso\"* (a túa rede de seguridade para que nunca quede un camiño sen retorno).\n\n### Lóxica booleana en minúsculas claras (`and`, `or`, `xor`, `not`)\nFixácheste en que moitas linguaxes usan `|` para bits, `||` para booleanos, `!` para negar... e cando les unha condición complexa parece que un gato camiñou sobre o teclado (`if (!a && (b || c))`)?\n\nEn Ridgun, como `|` xa é o símbolo sagrado das gardas e dos tipos alxébricos, e `||` é o operador de paralelismo, **os operadores booleanos escríbense con palabras claras en minúsculas**:\n\n- **`and`**: cúmprense as dúas cousas con avaliación en curtocircuíto (`idade >= 18 and ten_carne`).\n- **`or`**: cúmprese polo menos unha con avaliación en curtocircuíto (`dia == \"sabado\" or dia == \"domingo\"`).\n- **`xor`**: cúmprese unha ou outra, pero non as dúas á vez.\n- **`not`**: inverte o booleano (`not (esta baleiro texto)`).\n- **`<`, `>`, `<=`, `>=`**: comparan tanto números (`Int`, `Float`) como cadeas **`String` en orde lexicográfica UTF-8** (`\"alfa\" < \"beta\"`).\n\nMira que limpo queda o clásico problema dos anos bisestos:\n\n```ridgun\ne bisesto Int => Bool\ne bisesto a =\n | (a % 400) == 0 = True\n | (a % 4) == 0 and (a % 100) != 0 = True\n | otherwise = False\n```\n\nCero símbolos crípticos. Leo alguén que non sabe Ridgun e enténdeo á primeira.\n\n### Variables locais condicionais (`x =` + `|`)\nComo en Ridgun todo `=` segue a mesma regra, **tamén podes usar `|` para asignar unha variable local** sen ter que crear unha función auxiliar á parte:\n\n```ridgun\nprezo billete Int Bool => Int\nprezo billete base e_familia_numerosa =\n desconto =\n | e_familia_numerosa = 20\n | base > 100 = 10\n | otherwise = 0\n base - (base * desconto / 100)\n```\n\n### Árbores de decisión aniñadas e o limitador compacto `|.`\n*\"E se dentro dunha rama `| cond =` ou `| otherwise =` quero abrir outra sub-árbore de gardas `|` de 3 ou 4 niveis de profundidade e logo volver a un nivel superior?\"*\n\nPodes aniñar `|` dentro de `|` tantos niveis como queiras. E cando un bloque `|` aniñado remata pero debaixo seguen máis ramas dun nivel pai (ou máis sentenzas do bloque), colocas **`|.`** ao final da súa última liña (`| otherwise = valor |.`) para indicar sen ningunha ambigüidade que esa sub-árbore se pecha e volves ao nivel anterior:\n\n```ridgun\nencaminar peticion Int Int Bool Bool Int Int => Int\nencaminar peticion metodo rol asinada e_admin tamano_kb limitado =\n | metodo == 0 =\n | tamano_kb == 0 = 204\n | otherwise = 200 |.\n | metodo == 1 =\n | not asinada = 401\n | otherwise =\n -- Nivel 2: petición POST asinada\n | limitado > 0 =\n -- Nivel 3: control de rate-limit\n | e_admin =\n -- Nivel 4: excepción para administradores\n | tamano_kb > 10000 = 413\n | otherwise = 202 |.\n | otherwise = 429 |.\n | tamano_kb > 1000 =\n -- Nivel 3: volvemos desde o Nivel 4 grazas a `|.`\n | rol >= 2 and e_admin = 201\n | otherwise = 413 |.\n | otherwise = 201 |.\n | otherwise = 405\n```\n\nFíxate no limpo que é:\n- Nunha árbore simple de 1 nivel (ou ao final de toda a función) **nunca fai falta poñer `|.`** (sería ruído innecesario).\n- Só escribes `|.` ao pechar unha rama aniñada cando debaixo continúa unha rama dun nivel superior.\n\n### Bloques de varias liñas dentro dunha garda (`| cond =` + bloque sangrado)\nIgual que despois do `=` dunha función normal podes abrir un bloque sangrado de varias liñas, **despois do `=` de calquera garda `| cond =` ou `| otherwise =` tamén podes facer un salto de liña e abrir un bloque secuencial**:\n\n```ridgun\naxustar fiestra Int Int => Int\naxustar fiestra idx limite =\n | idx >= limite =\n tope = limite - 1\n escalado = tope * 2\n escalado + 10\n | otherwise =\n seguinte = idx + 1\n seguinte * 2\n```\n\nCada rama da garda ten o seu propio bloque local limpo e a súa última liña é o valor que devolve esa rama.\n\n---\n\n## 4.3. \"Sen `while`? E non vai rebentar a pila se percorro un ficheiro grande?\"\n\nFalemos do elefante na sala.\n\nSe vés de Python, Java ou JavaScript, ensináronche desde pequeniño que *\"a recursión é elegante no encerado da universidade, pero no mundo real desborda a pila (`StackOverflowError`) ás 1.000 voltas; usa un bucle `while`\"*.\n\nIso é verdade en linguaxes que **non** optimizan as chamadas de cola. Pero nunha linguaxe funcional de sistemas como Ridgun, **unha recursión de cola É un bucle `while` disfrazado de función pura**.\n\n### Que diferenza hai entre unha recursión normal e unha recursión de cola?\nFíxate nestas dúas funcións que contan cara a atrás desde `n` ata `0` sumando os números:\n\n```ridgun\n-- Versión A: Recursión NORMAL (deixa traballo pendente para a volta)\nsumar lento Int => Int\nsumar lento 0 = 0\nsumar lento n = n + (sumar lento (n - 1))\n\n-- Versión B: Recursión DE COLA (leva a conta ao día en `acumulado`)\nsumar rapido loop Int Int => Int\nsumar rapido loop 0 acumulado = acumulado\nsumar rapido loop n acumulado = sumar rapido loop (n - 1) (acumulado + n)\n```\n\n- Na **Versión A**, antes de poder sumar `n + ...`, a función ten que agardar a que remate `sumar lento (n - 1)`. Iso obriga ao ordenador a gardar `n` na pila en cada paso.\n- Na **Versión B**, a chamada a `sumar rapido loop` **é o último que ocorre na liña**. Non queda nada pendente detrás dela. Xa lle pasas a suma actualizada en `acumulado + n`.\n\n### O truco de maxia de `ridgunc` (TCO: *Tail-Call Optimization*)\nCando `ridgunc` compila `sumar rapido loop`, ve que a función se chama a si mesma ao final da cláusula e di:\n\n> *\"Para que vou crear un marco de pila novo se o actual xa non serve para nada? Vou sobrescribir `n` con `n - 1`, sobrescribir `acumulado` con `acumulado + n` e saltar ao principio cun `loop { ... continue; }` nativo.\"*\n\nResultado:\n1. Podes chamar a `sumar rapido loop 100_000_000 0` (cen millóns de voltas) e **gastará exactamente 0 bytes extra de pila**.\n2. Vai igual de rápido ca un bucle `while` escrito á man en Rust ou C, pero sen ter que declarar nin unha soa variable mutable no teu código Ridgun.\n\n---\n\n## 4.4. O patrón mestre que usarás en todo programa Ridgun\n\nCada vez que noutra linguaxe pensarías *\"vou facer un bucle `while` cun índice `i` e un acumulador\"*, en Ridgun escribes unha parella de funcións coma esta (a función principal limpa e a súa axudante `_loop` de cola):\n\n```ridgun\nbuscar caracter loop String Int Int Int => Int\nbuscar caracter loop texto pos fin buscado =\n | pos >= fin = -1\n | (str char at texto pos) == buscado = pos\n | otherwise = buscar caracter loop texto (pos + 1) fin buscado\n\nbuscar caracter String Int => Int\nbuscar caracter texto buscado = buscar caracter loop texto 0 (str len texto) buscado\n```\n\nMira as tres gardas de `buscar caracter loop`:\n1. Cheguei ao final (`pos >= fin`)? Devolvo `-1` (fin do bucle).\n2. Atopei o carácter en `pos`? Devolvo `pos` (fin do bucle).\n3. Non é este? Paso ao seguinte (`pos + 1`) cunha chamada de cola (que o compilador converte en `continue;`).\n\nLimpo, imposible de ter *bugs* de variables globais pisadas e rápido coma un raio.\n\nNo **Capítulo 05** ímoslle meter man ás coleccións de datos: **listas, tuplas, funcións que aceptan infinitos argumentos (`...`) e as máscaras de secuencias estilo J/APL**.\n"}, {"num":"05","slug":"05-listas-tuplas-e-secuencias","file":"05-listas-tuplas-e-secuencias.md","title":"Capítulo 05: Listas, Tuplas e o truco ninja das Secuencias","short_title":"Listas, Tuplas e o truco ninja das Secuencias","markdown":"# Capítulo 05: Listas, Tuplas e o truco ninja das Secuencias\n\nUn programa que só move un número ou un texto á vez serve de pouco. Na vida real manexas listas de ficheiros, filas dunha base de datos, paquetes de rede ou coordenadas.\n\nPara iso, Ridgun dáche tres envases distintos, cada un pensado para un traballo moi concreto:\n1. **Listas (`[T]`)**: *\"Teño moitos elementos do mesmo tipo (ou ningún) e quero percorrelos\"*.\n2. **Tuplas (`(A, B)`)**: *\"Quero devolver un par ou un trío de cousas distintas xuntas sen crear un tipo novo\"*.\n3. **Secuencias e máscaras (`s(_ + _)`, `(+ _ _ +)`)**: *\"Quero filtrar ou reordenar argumentos ao voo coma se tivese un coador matemático\"*.\n\nImos unha por unha.\n\n---\n\n## 5.1. Listas (`[T]`): O pan de cada día\n\nEn Ridgun, se pos corchetes arredor dun tipo, tes unha lista dese tipo:\n- `[Int]` é unha lista de enteiros: `[10, 20, 30]`.\n- `[String]` é unha lista de textos: `[\"maza\", \"pera\"]`.\n- `[]` é a lista baleira (o comodín que encaixa con calquera `[T]`).\n\n> *\"Ouve, podo meter un número e un texto xuntos na mesma lista tipo `[1, \"dous\"]`?\"*\n> Tal cal, **non** (e dálle as grazas ao compilador por impedircho, porque así nunca che rebentará unha suma en produción ao atoparse un texto sorpresa). No Capítulo 06 veremos como resolver o caso de `[1, \"dous\"]` con elegancia total usando un Tipo Alxébrico (igual que fai Haskell).\n\n### Como se anaquea unha lista sen usar índices `i`, `j`, `k`: `[]` e `(x:xs)`\nNas linguaxes imperativas, percorrer unha lista adoita implicar `for (int i = 0; i < lista.size(); i++)`, co clásico risco de pasarte por un (*off-by-one*).\n\nEn Ridgun non contas índices; **desmontas a lista pola cabeza**:\n- `[]`: encaixa se a lista está baleira.\n- `(x:xs)`: encaixa se a lista ten polo menos un elemento. Mete o primeiro elemento en `x` (a **cabeza**) e o resto da lista en `xs` (a **cola**).\n\nMira que gusto dá sumar unha lista ou contar cantos elementos ten:\n\n```ridgun\nsumar lista [Int] => Int\nsumar lista [] = 0\nsumar lista (x:xs) = x + (sumar lista xs)\n\ncontar elementos [A] => Int\ncontar elementos [] = 0\ncontar elementos (_:xs) = 1 + (contar elementos xs)\n```\n\nE un detalle técnico que che alegrará o día: en Ridgun, sacar a cola `xs` en `(x:xs)` **non copia o resto da lista en memoria**. Internamente é unha vista sobre o mesmo vector que só avanza o punteiro de inicio en `1` ($O(1)$ instantáneo).\n\n### Podes destapar varios elementos de golpe\nO patrón `:` dá moitísimo xogo cando estás analizando comandos ou protocolos:\n\n```ridgun\ninterpretar comando [String] => String\ninterpretar comando [] = \"Non escribiches nada\"\ninterpretar comando (\"axuda\":_) = \"Mostrando a axuda...\"\ninterpretar comando (\"copiar\":orixe:dest:[]) = \"Copiando de {orixe} a {dest}\"\ninterpretar comando (cmd:_) = \"Comando descoñecido: {cmd}\"\n```\n\nMira a terceira fila: `(\"copiar\":orixe:dest:[])` comproba dun só golpe que a lista teña **exactamente 3 elementos**, que o primeiro sexa `\"copiar\"`, e déixache o segundo e o terceiro servidos en bandexa en `orixe` e `dest`. Intenta escribir iso en C ou Java e verás cantas liñas de `args.length == 3 && args[0].equals(\"copiar\")` che saen.\n\n### Unir listas con dobres dous puntos (`::`)\nCando estás no lado dereito do `=` (nunha expresión) e queres pegar dúas listas, usas **`::`**:\n\n```ridgun\npegar listas [Int] [Int] => [Int]\npegar listas a b = a :: b\n\nengadir detras [Int] Int => [Int]\nengadir detras xs x = xs :: [x]\n```\n\n*(Regra mnemotécnica rápida: un só `:` nos patróns desmonta a lista; dous `::` nas expresións pegan dúas listas).*\n\nE se de verdade queres sacar o elemento número 5 directamente sen percorrer nada? Usa corchetes pegados ao nome: `mina_lista[5]`.\n\n---\n\n## 5.2. Funcións variádicas (`T...`): \"Pásame todos os que queiras!\"\n\nÁs veces queres que quen chame á túa función poida escribir `maximo de 4 19 2 88 7` comodamente, sen obrigalo a envolver os números entre corchetes `[4, 19, 2, 88, 7]`.\n\nPon tres puntos **`...`** detrás do último tipo da túa sinatura, e dentro da túa función recibirálo automaticamente empaquetado coma unha lista:\n\n```ridgun\nsumar varios Int... => Int\nsumar varios nums = sumar lista nums\n\nmain => ()\nmain =\n echo (sumar varios 10 20 30) -- Imprime 60\n echo (sumar varios 1 2 3 4 5 6 7) -- Imprime 28\n```\n\n---\n\n## 5.3. Tuplas: Cando unha función quere devolver máis dunha cousa\n\nEn moitas linguaxes devolver dúas cousas desde unha función é unha dor: ou creas unha clase auxiliar parva `ResultadoConPosicion`, ou pasas punteiros de saída por referencia.\n\nEn Ridgun devolves unha **tupla** `(A, B)`:\n\n```ridgun\ndividir e resto Int Int => (Int, Int)\ndividir e resto a b = (a / b, a % b)\n```\n\nMesmo podes aforrar os parénteses na liña da sinatura se che resulta máis limpo:\n`dividir e resto Int Int => Int Int`.\n\n### Como abrir unha tupla\nIgual que a creas con `(a, b)`, ábrela nos argumentos de calquera función con `(cociente, resto)`:\n\n```ridgun\nexplicar division (Int, Int) => String\nexplicar division (cociente, resto) = \"Cabe a {cociente} e sobran {resto}\"\n```\n\n### O detalle estrela: Desempaquetado automático entre funcións\nSupón que tes `dividir e resto` (que devolve unha tupla de 2 enteiros) e outra función `sumar par Int Int => Int` (que agarda 2 enteiros soltos). En Ridgun non tes que abrir a tupla á man nunha variable intermedia; podes enchufar unha función dentro da outra directamente:\n\n```ridgun\nsumar par Int Int => Int\nsumar par x y = x + y\n\nmain => ()\nmain =\n -- dividir e resto 17 5 devolve (3, 2), e sumar par recibe 3 e 2 directamente:\n echo (sumar par (dividir e resto 17 5)) -- Imprime 5\n```\n\n---\n\n## 5.4. O truco ninja: Secuencias e Máscaras Posicionais (`s(_ + _)`, `(+ _ _ +)`)\n\nAquí vén unha das xoias máis orixinais de Ridgun, herdada directamente das linguaxes vectoriais como J e APL.\n\nImaxina que recibes unha restra de argumentos co patrón de secuencia **`(s)`** e queres quedar só con algúns e descartar outros sen declarar cinco variables con nomes inventados (`_a`, `b`, `_c`, `d`...).\n\nAplícaslle a `s` unha **máscara de tupla con `+` (queda con el) e `_` (descártao)**:\n\n- **`s(_ + _)`**: de 3 elementos, descarta o primeiro (`_`), queda co segundo (`+`) e descarta o terceiro (`_`).\n- **`s(+ _ +)`**: queda co primeiro e o terceiro; devolve a secuencia con eses dous.\n- **`s(_ _ +...)`**: descarta os dous primeiros (`_ _`) e aplica `+...` **a todos os demais que veñan detrás**, sexan cantos sexan.\n\nMírao funcionando:\n\n```ridgun\no do medio Int Int Int => Int\no do medio (s) = s(_ + _)\n\ndescartar dous primeiros Int... => [Int]\ndescartar dous primeiros (s) = s(_ _ +...)\n\nmain => ()\nmain =\n echo (o do medio 100 42 999) -- Imprime 42\n echo (descartar dous primeiros 1 2 10 20 30) -- Imprime [10, 20, 30]\n```\n\nE o mellor de todo: podes intercalar unha máscara posicional **`(+ _ _ +)`** **directamente no medio dunha tubaxe `->`**! Se unha función devolve unha tupla de varios valores e a seguinte función só precisa dous deles, filtras a tupla ao voo e Ridgun desempaquetará os elementos superviventes como argumentos separados da seguinte etapa:\n\n```ridgun\n-- parsear paquete devolve 4 valores: (Int, Int, [String String], String)\n-- (+ _ _ +) queda co 1º (code) e o 4º (msg), e pásaollos a combinar codigo:\nparsear paquete \"v0.9.9\" -> (+ _ _ +) -> combinar codigo -> resumo\n```\n\nAdemais, se usas o operador coma `,` fóra de parénteses (`lista1 , lista2`), Ridgun aplana ambas coleccións nunha única secuencia continua.\n\nNo **Capítulo 06** imos dar o paso que converte a un programador funcional nun arquitecto de primeira: **crear os teus propios Tipos de Datos Alxébricos (ADTs), escribir funcións polimórficas con `A`..`Z` e fabricar os teus propios operadores á medida**.\n"}, {"num":"06","slug":"06-tipos-alxebricos-e-polimorfismo","file":"06-tipos-alxebricos-e-polimorfismo.md","title":"Capítulo 06: Tipos de Datos Alxébricos (ADTs), Polimorfismo e por que aquí non queremos `null` nin `Any`","short_title":"Tipos de Datos Alxébricos (ADTs), Polimorfismo e por que aquí non queremos `null` nin `Any`","markdown":"# Capítulo 06: Tipos de Datos Alxébricos (ADTs), Polimorfismo e por que aquí non queremos `null` nin `Any`\n\nEn 1965, Tony Hoare inventou a referencia nula (`null` / `nil`). Corenta anos despois pediu perdón publicamente e chamouna *\"o meu erro de mil millóns de dólares\"*.\n\nPor que? Porque nas linguaxes con `null`, unha función prométeche na súa sinatura que che vai devolver un `Usuario`... e cando menos o agardas devólveche `null` e bótache abaixo o servidor ás tres da mañá.\n\nE outro tanto ocorre coa herdanza de obxectos e o tipo `Any` (ou `Object`): metes un número e un texto nunha lista `[1, \"dous\"]`, a linguaxe encolle os ombreiros e di *\"vale, pois agora isto é unha `List[Any]`, boa sorte adiviñando que hai dentro cando a percorras\"*.\n\nEn Ridgun cortamos eses dous problemas de raíz con **Tipos de Datos Alxébricos (ADTs)** e **Polimorfismo Paramétrico**. Soa a nome de materia dura, pero en cinco minutos vas ver que é a forma máis natural do mundo de modelar datos.\n\n---\n\n## 6.1. Tipos Produto (Rexistros): Os teus `struct` de toda a vida, pero con superpoderes\n\nCando queres agrupar varios datos que pertencen a unha mesma entidade (por exemplo, un servidor co seu nome, a súa IP e o seu porto), defines un **`type`** cos seus campos:\n\n```ridgun\ntype Servidor = host: String, porto: Int, tls_activo: Bool\n```\n\nCon só escribir esa liña, Ridgun agasállache **catro cousas** automaticamente:\n\n### 1. O construtor `Servidor`\n```ridgun\nmeu nodo => Servidor\nmeu nodo = Servidor \"api.exemplo.com\" 443 True\n```\n\n### 2. Unha función *getter* pura para cada campo (`host`, `porto`, `tls_activo`)\nNon tes que escribir `s.getHost()` nin nada parecido; cada nome de campo é directamente unha función que extrae ese dato:\n\n```ridgun\nurl conexion Servidor => String\nurl conexion srv = \"Conectar a \" + (host srv) + \" no porto {porto srv}\"\n```\n\n### 3. Actualización funcional de rexistros (`with campo: valor`)\nImaxina un rexistro de configuración con 12 ou 15 campos (como as opcións dun comando `ls` ou dun servidor web) e queres mudar só un ou dous campos sen tocar o resto. En vez de reconstruír o rexistro pasando os 15 campos á man, usas **`with`** e dous puntos **`:`** (exactamente igual ca na definición do `type`):\n\n```ridgun\nactivar modo dev Servidor => Servidor\nactivar modo dev srv = srv with porto: 8080, tls_activo: False\n```\n\nO mellor dos dous mundos:\n- No teu código é **100% inmutable e seguro** (o compilador comproba que `porto` e `tls_activo` existan en `Servidor` e teñan o tipo correcto).\n- Por baixo, se `srv` non está compartido con ninguén máis, o executable nativo reutiliza a súa memoria *in-place* (`Arc::try_unwrap`) sen copiar o resto de campos.\n\n### 4. *Pattern matching* completo por se queres abrir o rexistro en canle\n```ridgun\ne porto seguro Servidor => Bool\ne porto seguro (Servidor _ 443 True) = True\ne porto seguro _ = False\n```\n\n---\n\n## 6.2. Tipos Suma (Variantes con `|`): Como xubilar a `null`, ás excepcións e á herdanza\n\nAgora vén o bo.\nUn rexistro normal ten `host` **E** `porto` **E** `tls_activo`.\nPero no mundo real moitas veces precisas dicir: *\"Un método de pagamento é **OU BEN** unha tarxeta co seu número, **OU BEN** unha transferencia co seu IBAN, **OU BEN** efectivo (que non leva ningún dato extra)\"*.\n\nEn orientación a obxectos terías que crear unha interface `MetodoPagamento`, tres subclases en tres ficheiros distintos, construtores, `instanceof`... En Ridgun é **unha liña**:\n\n```ridgun\ntype MetodoPagamento = Tarxeta String Int | Transferencia String | Efectivo\n```\n\nE mira que pasa cando procesas un `MetodoPagamento` nunha función:\n\n```ridgun\ndescribir pagamento MetodoPagamento => String\ndescribir pagamento (Tarxeta num _) = \"Pagamento con tarxeta rematada en {num}\"\ndescribir pagamento (Transferencia iban) = \"Transferencia desde a conta {iban}\"\ndescribir pagamento Efectivo = \"Pagamento en metálico en portelo\"\n```\n\nCada variante (`Tarxeta`, `Transferencia`, `Efectivo`) leva pegados unicamente os datos que teñen sentido para ela. É **imposible** construír un pagamento en `Efectivo` que teña un `iban` colgado por erro.\n\n---\n\n## 6.3. \"E como meto `[1, \"dous\"]` nunha lista sen usar `Any`?\"\n\nVolvamos á pregunta do millón. Imaxina que estás lendo un ficheiro CSV ou JSON onde unha cela pode ser un número enteiro, un texto ou estar baleira.\n\nSe tiveses un tipo `Any`, meterías `[1, \"dous\"]` e perderías toda a axuda do compilador: a partir dese momento calquera operación sobre a lista sería camiñar cos ollos vendados.\n\nEn Ridgun (igual ca en Haskell ou Rust), creas en dous segundos un tipo suma que explica exactamente que cousas poden vivir dentro desa lista:\n\n```ridgun\ntype ValorCela = Enteiro Int | Texto String | Baleiro\n\nformatar cela ValorCela => String\nformatar cela (Enteiro n) = \"Número: {n}\"\nformatar cela (Texto s) = \"Texto: {s}\"\nformatar cela Baleiro = \"(baleiro)\"\n\nmina fila => [ValorCela]\nmina fila = [Enteiro 1, Texto \"dous\", Baleiro]\n```\n\nVes a diferenza?\n- Con `Any`, o compilador non sabe se na lista hai enteiros, cadeas ou unha cafeteira conectada por USB.\n- Con `[ValorCela]`, podes mesturar números e textos na mesma lista, **pero o compilador sabe ao 100% que só existen esas tres posibilidades** e obrígate a desempaquetar cada caso (`Enteiro n`, `Texto s`, `Baleiro`) antes de usalo. Seguridade total sen perder nin un chisco de flexibilidade.\n\n---\n\n## 6.4. `Option` e `Result`: Cando algo pode non estar ou pode fallar\n\nComo en Ridgun non existe `null` nin existen excepcións que salten polos aires (`throw new Exception()`), a linguaxe trae integrados dous tipos suma universais:\n\n1. **`Option`**: ou tes **`Some valor`**, ou tes **`None`** (nada).\n2. **`Result`**: ou a operación saíu ben (**`Ok valor`**), ou fallou por un motivo concreto (**`Err erro`**).\n\n```ridgun\nbuscar division Int Int => Result\nbuscar division _ 0 = Err \"Ei! Non se pode dividir por cero\"\nbuscar division a b = Ok (a / b)\n\nimprimir res Result => ()\nimprimir res (Ok n) = echo \"O resultado é {n}\"\nimprimir res (Err msg) = echo \"Fallo controlado: {msg}\"\n```\n\nNinguén pode ignorar un erro por descoido porque o propio tipo `Result` obrígate a mirar se dentro hai un `Ok` ou un `Err`.\n\n---\n\n## 6.5. Polimorfismo Paramétrico: Funcións xenéricas con `A`, `B`, `T`...\n\nSupón que escribes unha función que inverte un par `(x, y)` ou que saca o primeiro elemento dunha lista. De verdade importa se a lista é de `Int`, de `String` ou de `Servidor`? Para nada! O algoritmo é idéntico.\n\nEn Ridgun, **calquera letra maiúscula individual (`A` a `Z`)** nunha sinatura de tipos funciona automaticamente como un **tipo comodín polimórfico (variable de tipo)**:\n\n```ridgun\ncabeza segura [A] A => A\ncabeza segura [] por_defecto = por_defecto\ncabeza segura (x:_) _ = x\n\nxirar par (A, B) => (B, A)\nxirar par (x, y) = (y, x)\n```\n\nFíxate na maxia de `cabeza segura [A] A => A`:\n- Non é un `Any` descontrolado: di que **o tipo da lista `[A]`, o tipo do valor `por_defecto` (`A`) e o tipo devolto (`A`) teñen que ser o mesmo**.\n- Se chamas a `cabeza segura [10, 20] 0`, o compilador ata cabos (`A = Int`) e devólveche un `Int`.\n- Se chamas a `cabeza segura [\"a\", \"b\"] \"ningun\"`, ata cabos (`A = String`) e devólveche un `String`.\n- Pero se intentas facer trampa e chamas a `cabeza segura [10, 20] \"ningun\"`, zas! O compilador fréate en seco porque o primeiro argumento di que `A` é `Int` e o segundo di que `A` é `String`.\n\n---\n\n## 6.6. Crea os teus propios operadores (`op ::`): Desde vectores ata frases á medida\n\nPor que só os números nativos ían ter dereito a usar `+` ou `*`? E por que conformarse cos operadores de sempre cando podes inventar a sintaxe perfecta para o teu problema?\n\nCon **`op ::`** (ou directamente sen prefixo se usas patróns de operador) podes facer dúas virguerías:\n\n### 1. Ensinarlle a `+`, `-`, `*`, `/`, `%` a operar cos teus propios tipos\n```ridgun\ntype Complexo = real: Int, imax: Int\n\nop :: Complexo + Complexo => Complexo\n a + b = Complexo ((real a) + (real b)) ((imax a) + (imax b))\n```\n\n### 2. Crear operadores *Mixfix* (que se len coma frases naturais, incluídos `type` e `with`)\nNunha sinatura de operador, **as palabras con maiúscula inicial (`Int`, `String`, `A`..`Z`) ou entre corchetes/parénteses (`[Int]`, `(A, B)`) son os ocos para os argumentos**, e **as palabras en minúscula son parte da frase do operador** (mesmo palabras tan habituais como `type` ou `with`, que en Ridgun son contextuais e nunca te bloquean):\n\n```ridgun\nop :: se Bool enton A senon A => A\n se True enton v senon _ = v\n se False enton _ senon 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 mensaxe = se (10 > 5) enton \"Dez gaña!\" senon \"Imposible\"\n echo mensaxe\n echo (format \"panel\" type \"bordered\" with 80)\n```\n\nAcabas de fabricar o teu propio operador ternario polimórfico e un DSL de formatado en **3 liñas** de Ridgun.\n\n---\n\n## 6.7. Construtores compartidos, variantes de tipo directo, `expand type` e espazos naturais en `fn ::`\n\nRidgun leva os tipos alxébricos e as funcións un paso máis alá en expresividade e extensibilidade:\n\n1. **Construtores compartidos entre distintos `type`**: Varios tipos suma poden reutilizar o mesmo nome de construtor (`Error`, `Var`, `Lit`) con igual ou distinta aridade; o verificador de tipos distíngueos pola columna da sinatura.\n2. **Variantes con nome de tipo directo (`type Something = String | Int | MyStruct Float Int`)**: Cando unha variante é un tipo primitivo (`String`, `Int`, `Float`/`Real`, `Bool`, `Char`), non precisas repetir `Int Int` nin `String String`. Podes pasar valores directos (`\"ola\"`, `42`) ou construtores explícitos (`String \"ola\"`, `Int 42`), e facer pattern matching tanto con literais (`\"admin\"`, `0`) como con patróns tipados (`(String s)`, `(Int n)`).\n3. **Extensión aberta de tipos (`expand type`) e fusión automática de cláusulas `fn ::`**: Con `expand type` podes engadir novas variantes a un tipo suma ou novos campos a un rexistro. Ao volver declarar `fn ::` para a mesma función, as novas cláusulas fusiónanse automaticamente **antes** de calquera cláusula comodín `_` (sen necesidade de escribir `expand fn`).\n4. **Espazos naturais `\" \"` en lugar de `_` en `fn ::`, funcións nativas e nomes de ficheiro**: Igual que en `op ::`, os nomes de función en `fn ::`, as chamadas a funcións nativas (`assert equals`, `str len`, `read file`) e os ficheiros `.rg` (`test core language.rg`) pódense escribir con espazos naturais:\n\n```ridgun\ntype Something = String | Int | MyStruct Float Int\n\nfn :: describir algo Something => String\n describir algo \"admin\" = \"admin_especial\"\n describir algo 0 = \"cero\"\n describir algo (String s) = \"texto:{s}\"\n describir algo (Int n) = \"enteiro:{n}\"\n describir algo (MyStruct ratio cnt) = \"estrutura:{ratio}:{cnt}\"\n describir algo _ = \"outro\"\n\nexpand type Something = Bool\n\nfn :: describir algo Something => String\n describir algo (Bool b) = \"booleano:{b}\"\n```\n\n### 5. Tipos xenéricos propios (`type Result T E`, `type Page T`) e espazos de nomes de módulo (`import ... as`, `mod.Tipo`)\nAdemais de funcións polimórficas, podes declarar **os teus propios tipos suma e rexistros xenéricos** con parámetros de tipo (`A`..`Z`), así como organizar tipos, construtores, campos e funcións co mesmo nome en distintos módulos sen colisión (`module ::`, `import db as d`, `http.Config`, `d.Config`). Cada módulo ten **visibilidade estrita por importación en 3 niveis**: resolve os nomes sen prefixo buscando primeiro en **(1) as súas propias declaracións** (incluíndo varios ficheiros que compartan `module :: nome`), despois en **(2) os seus `import` directos** e só en último lugar en **(3) os seus `import` transitivos**, de xeito que outros módulos irmáns ou a aplicación principal nunca contaminan unha librería importada:\n\n```ridgun\ntype Resultado T E = Exito T | Fallo E\ntype Paxina T = elementos: [T], total: Int\n\nfn :: desempaquetar exito (Resultado A String) A => A\n desempaquetar exito (Exito v) _ = v\n desempaquetar exito (Fallo _) d = d\n```\n\nNo **Capítulo 07** imos poñer todos os núcleos do teu procesador a traballar á vez sen derramar unha pinga de suor: **funcións de orde superior (`map`, `||map`, `filter`, `||filter`, `fold`), clausuras léxicas (`(x => expr)`), grafos paralelos `||` con `->` e canles concorrentes**.\n"}, {"num":"07","slug":"07-orde-superior-e-paralelismo-dag","file":"07-orde-superior-e-paralelismo-dag.md","title":"Capítulo 07: Funcións de Orde Superior e Paralelismo sen Suor (Nin `Mutex`, nin `Deadlocks`)","short_title":"Funcións de Orde Superior e Paralelismo sen Suor (Nin `Mutex`, nin `Deadlocks`)","markdown":"# Capítulo 07: Funcións de Orde Superior e Paralelismo sen Suor (Nin `Mutex`, nin `Deadlocks`)\n\nEn case todas as linguaxes de programación hai un momento dramático na vida dun proxecto: o día en que alguén di nunha xuntanza:\n\n> *\"Isto vai lento. Hai que paralelizalo con fíos.\"*\n\nFaise o silencio na sala. Todo o mundo sabe o que vén despois: semanas cazando *race conditions* pantasma que só ocorren os martes con lúa chea, bloqueos mutuos (*deadlocks*) e erros irreproducibles.\n\nEn Ridgun, cando alguén di *\"hai que paralelizalo\"*, abonda con antepoñer `||` ás túas operacións de lista (**`||map`** e **`||filter`**) ou poñer os teus cálculos nun bloque paralelo `||` con frechas `->` e vas tomar un café.\n\nPor que? Porque **cando as túas funcións son puras e os teus datos son inmutables por defecto, dous núcleos de CPU traballando á vez xamais poden pisarse a mangueira**.\n\nImos velo paso a paso.\n\n---\n\n## 7.1. Funcións que reciben funcións: `map`, `||map`, `filter`, `||filter` e `fold`\n\nEn Ridgun unha función é un valor máis. Podes pasala por parámetro igual que pasas o número `7`.\n\nA linguaxe trae de serie os combinadores de coleccións para transformar listas sen escribir nin unha soa recursión manual, tanto en modo secuencial (`map`, `filter`, `fold`) como en modo paralelo multicore co prefixo `||` (**`||map`**, **`||filter`**):\n\n### 1. `map` (secuencial) e `||map` (paralelo multicore)\nSupón que tes unha función `custoso Int => Int` e unha lista con números:\n\n```ridgun\ncustoso Int => Int\ncustoso x = (x * x) + (x * 3)\n\nmain => ()\nmain =\n datos = [1, 2, 3, 4, 5, 6, 7, 8]\n -- Secuencial estrito de esquerda a dereita:\n r1 = map custoso datos\n -- Paralelo multicore co prefixo `||`:\n r2 = ||map custoso datos\n echo r1\n echo r2\n```\n\n- Con **`map`**, Ridgun aplica a función elemento a elemento de esquerda a dereita no fío actual (ideal para listas pequenas ou funcións con efectos de entrada/saída ordenados).\n- Con **`||map`**, o runtime de Ridgun reparte os elementos de `datos` entre os fíos traballadores do teu procesador e reconstrúe a lista final **exactamente na mesma orde**, garantindo que `||map f xs == map f xs`.\n\n### 2. `filter` (secuencial) e `||filter` (paralelo): Quedar só cos que cumpran unha condición\nPásaslle unha función que devolva `Bool` (un *predicado*) e unha lista:\n\n```ridgun\ne positivo Int => Bool\ne positivo n = n > 0\n\nmain => ()\nmain =\n limpos = filter e positivo [-5, 10, 0, -2, 42]\n limpos_par = ||filter e positivo [-5, 10, 0, -2, 42]\n echo limpos -- Imprime [10, 42]\n echo limpos_par -- Imprime [10, 42]\n```\n\n### 3. `fold`: Reducir toda unha lista a un só resultado\nPásaslle unha función combinadora de dous argumentos `(Acumulado Elemento => Acumulado)`, o valor inicial e a lista:\n\n```ridgun\nacumular produto Int Int => Int\nacumular produto acc x = acc * x\n\nmain => ()\nmain =\n echo (fold acumular produto 1 [2, 3, 4, 5]) -- 1 * 2 * 3 * 4 * 5 = 120\n```\n\n### Como escribo eu a miña propia función que reciba outra función?\nMoi doado: na sinatura de tipos pos a sinatura da función convidada entre parénteses `(Entrada => Saida)`:\n\n```ridgun\nexecutar duas veces (A => A) A => A\nexecutar duas veces f x = f (f x)\n```\n\n### 4. Tubaxes fluídas de esquerda a dereita (`->`) — O estilo 100% Ridgun!\nCando encadeas varias transformacións (`map`, `filter`, `fold` ou as túas propias funcións e operadores `op ::`), aniñar parénteses de dereita a esquerda (`compact l (filter g (map f xs))`) obrígache a ler de dentro cara a fóra.\n\nEn Ridgun, o **estilo idiomático recomendado (`2b`)** lese de esquerda a dereita co operador de tubaxe **`->`**, onde **o que vén da esquerda entra sempre como primeiro argumento** da función ou `op ::` seguinte:\n\n```ridgun\n-- Estilo idiomático recomendado en Ridgun (2b):\nxs map (x => x * 2) -> filter (x => x > 10) -> fold (acc x => acc + x) 0 -> resultado\n\n-- E se unha función devolve unha tupla, fíltrala cunha máscara e desempaquétase soa:\nparsear paquete \"v0.9.9\" -> (+ _ _ +) -> combinar codigo -> resumo\n```\n\nAdemais, calquera expresión con efectos secundarios (escribir un ficheiro, invocar o sistema) pódese poñer soa na súa liña sen `echo` nin variables mudas `_ =`, ou rematar en `-> ()` se queres descartar explicitamente o seu retorno.\n\n### 5. Clausuras léxicas e funcións anónimas (`(x => expr)`, `(acc x => expr)`)\nCando queres pasar unha función curta ao voo ou capturar variables locais do teu ámbito actual (como un desprazamento, un prefixo ou unha sesión activa) sen declarar unha función global auxiliar, usas unha **clausura léxica** entre parénteses con **`=>`**:\n\n```ridgun\nfn :: procesar con sesion => [Int]\n procesar con sesion =\n base = 10\n bono = 3\n sumados = map (x => x + base) [1, 2, 3]\n filtrados = filter (x => x >= bono) [1, 2, 3, 4]\n total = fold (acc x => acc + x + bono) 0 [1, 2, 3]\n pares = map ((a b) => a + b + base) [(1, 2), (3, 4)]\n sumados :: filtrados :: [total] :: pares\n```\n\nCinco regras manteñen as lambdas limpas e sen ambigüidade:\n- **Parénteses obrigatorias**: toda lambda vai sempre entre parénteses `(... => ...)`.\n- **Lambda sen parámetros (`(=> expr)`)**: igual que `fn :: main => ()` non leva `()` antes de `=>`, unha lambda sen parámetros escríbese `(=> expr)` (e o seu tipo é `(=> Tipo)`), nunca `(() => expr)`.\n- **Varios parámetros fronte a tupla (`(a b => ...)` vs. `((a b) => ...)`)**: `(acc x => ...)` recibe dous parámetros separados; `((a b) => ...)` recibe unha soa tupla e desestrutúraa.\n- **Corpo de varias liñas**: se pos un salto de liña tras `=>`, podes escribir un bloque sangrado de varios pasos antes do `)` de peche:\n ```ridgun\n dobres = map (x =>\n paso = x * 2\n paso + base\n ) [1, 2, 3]\n ```\n- **Maiúsculas fronte a minúsculas**: `(Conn => Conn)` é un tipo de función (inicial maiúscula) e `(conn => conn)` é un valor lambda (inicial minúscula).\n\nAs variables capturadas (`base`, `bono`) empaquétanse automaticamente con xestión determinista de memoria (`RMM1`/`RMM2`), sen fugas e sen pausas de colector de lixo.\n\n---\n\n## 7.2. A xoia da coroa: Bloques `||` con `->` como Grafos Paralelos (DAGs)\n\nAquí é onde Ridgun muda a túa forma de pensar.\n\nNunha linguaxe imperativa normal, cando escribes tres liñas seguidas:\n1. Primeiro execútase a liña 1.\n2. Logo a liña 2 (aínda que non teña nada que ver coa liña 1).\n3. Logo a liña 3.\n\nIso é obrigar o ordenador a traballar con anteolleiras. En Ridgun, cando usas barras paralelas **`||`** con frechas **`->`** (sen necesidade de escribir ningunha palabra chave como `where`), non estás dando ordes secuenciais: estás debuxando un **Grafo Acíclico Dirixido (DAG)** que di que cálculo alimenta a que 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 calculo => Int\nsuper calculo = rama_esquerda + rama_dereita\n || fib 10 -> rama_esquerda\n || fact 6 -> rama_dereita\n```\n\nLeo en voz alta:\n- *\"Quero calcular `rama_esquerda + rama_dereita`, mentres en paralelo (`||`) `fib 10` vai a `rama_esquerda` (`->`) e `fact 6` vai a `rama_dereita` (`->`).\"*\n\n### O que fai `ridgunc` entre bastidores coas túas liñas `||`:\n1. Mira as frechas `->` e arma a árbore de dependencias (DAG).\n2. **Caza ciclos estaticamente**: se por erro escribes `|| a + 1 -> b` e `|| b + 1 -> a`, o compilador diche no acto *\"Quietos! Hai unha dependencia circular\"* e non che deixa compilar un colgue infinito.\n3. Ve que `fib 10` e `fact 6` non dependen o un do outro, así que teñen luz verde para correr en paralelo antes de sumarse en `rama_esquerda + rama_dereita`.\n\nMesmo podes montar unha tubaxe de varias plantas dentro do mesmo bloque `||` —xa sexa ao pé da expresión ou xusto despois do `=`— e o compilador deducirá el só que plantas poden ir á vez e cales teñen que agardar:\n\n```ridgun\nbalance trimestral Int => Int\nbalance trimestral base =\n || base * 100 -> vendas_brutas\n || base * 40 -> custos_fixos\n || vendas_brutas - custos_fixos -> beneficio_bruto ||.\n beneficio_bruto * 80 / 100\n```\n\nFíxate no detalle **`||.`** ao final da última liña `||`: igual que `|.` pecha unha árbore de gardas aniñada, **`||.`** pecha o bloque paralelo `||` cando debaixo seguen sentenzas secuenciais normais. Aquí `vendas_brutas` e `custos_fixos` arrincan en paralelo; cando ambos están listos calcúlase `beneficio_bruto`, e finalmente devólvese `beneficio_bruto * 80 / 100`. Todo sen un só `async`, `Future`, `Promise` nin `Mutex`.\n\n---\n\n## 7.3. O atallo `||` sen nomes: Tubaxes paralelas que devolven unha tupla\n\nE se só queres lanzar dous ou tres cálculos en paralelo e recollelos xuntos nunha tupla sen sequera inventar nomes de variables con `->`?\n\nUsas `||` directamente:\n\n```ridgun\npar paralelo Int => (Int, Int)\npar paralelo n =\n || fib n\n || fact n\n```\n\nDirecto, visual e devolve a tupla `(fib n, fact n)`.\n\n---\n\n## 7.4. Cando queres o volante manual: o Modelo Nativo de Axentes (`agent`, `send ... to`, `receive from`) e Canles CSP\n\nO paralelismo automático de `||` e `||map` cobre o 90% dos casos. Pero cando programas un servidor persistente, sesións de usuario con estado illado, filas de traballos ou unha tubaxe produtor-consumidor, queres **axentes concorrentes con memoria illada e paso de mensaxes tipado**.\n\nEn Ridgun non fai falta inventar unha sintaxe estraña como `actor ::`: **calquera función normal (`fn ::` ou `op ::`) convértese nun axente concorrente nunha liña con `agent`**:\n\n### 1. Axentes sen estado e con estado illado (`agent`, `send ... to`, `receive from`, `stop`)\n- `ag = agent (miña funcion)`: converte unha función `M => R` nun axente sen estado de tipo `Agent M R`.\n- `ag = agent (funcion con estado) estado_inicial`: converte unha función `S M => S` (ou `S M => S R`) nun axente con estado illado que lembra o seu estado mensaxe tras mensaxe sen un só `Mutex`.\n- `send msg to ag` (ou con varios argumentos: `send a b to ag`): envía unha mensaxe tipada á caixa de correo do axente.\n- `res = receive from ag` (ou `try receive from ag` sen bloquear): recolle a seguinte resposta da caixa de saída do axente.\n- `stop ag`: detén o axente e libera o seu estado automaticamente mediante `RMM1`/`RMM2`.\n\n```ridgun\nfn :: sumar visita Int Int => Int\n sumar visita acum n = acum + n\n\nfn :: procesar lote Int Int => Int\n procesar lote id t = (id * 100) + t\n\nfn :: main => ()\n main =\n contador = agent (sumar visita) 10\n send 5 to contador\n send 7 to contador\n v1 = receive from contador\n v2 = receive from contador\n stop contador\n\n traballador = agent (procesar lote)\n send 3 25 to traballador\n lote = receive from traballador\n stop traballador\n echo \"Visitas:\" v1 \"->\" v2 \"| Lote:\" lote\n```\n\n### 2. Elección intelixente en tempo de compilación (*Green Threads* vs. Fíos Reais do Kernel)\nUnha pregunta habitual é: *\"Os bloques `||`, `||map` e os axentes `agent` crean sempre fíos do sistema operativo ou corren como tarefas lixeiras?\"*\n\nEn Ridgun, **o compilador toma esa decisión por ti analizando estaticamente cada función**:\n- **Funcións lixeiras (`TierTrivial` / `StratInlineSequential`)**: Se a función que pasas a `||`, `||map`, `||filter` ou `agent` ten pouca profundidade (aritmética, formatado de cadeas, transición de estado rápida dunha sesión HTTP), quentar fíos do sistema operativo custaría máis có propio cálculo. O compilador execútaa como un paso *green-thread* ultralixeiro sen sobrecarga de `SYS_clone`.\n- **Cálculos pesados ou recursivos profundos (`TierDynamicRecursive` / `TierHeavyCPU`)**: Se a función ten recursión profunda (como `fib` ou `fact`) ou álxebra de tensores (`einsum`, `hadamard`, `@`), o compilador xera automaticamente o despacho multinúcleo sobre **fíos reais do kernel (`SYS_clone`)**, procesando as ramas `||` ou a cola de mensaxes do `agent` en paralelo real sobre todos os teus núcleos de CPU.\n\n### 3. Pasar mensaxes por canles acoutadas (`new bounded channel`)\nIgual ca en Go ou Erlang, ademais de axentes tamén podes crear tubos explícitos para comunicar etapas:\n\n- `new bounded channel capacidade`: crea unha canle con espazo para `capacidade` mensaxes.\n- `send through valor canle` (ou `chan send valor canle`): mete unha mensaxe no tubo.\n- `receive from canle` (ou `chan recv canle`): saca a seguinte mensaxe do tubo.\n- `try receive from canle`: mira se hai mensaxe sen quedar agardando; devolve `Some valor` ou `None`.\n- `receive from timeout canle ticks`: agarda un límite de tempo e devolve `Ok valor` ou `Err Timeout`.\n\n```ridgun\nfn :: main => ()\n main =\n tubo = new bounded channel 4\n _ = send through \"Tarefa A completada\" tubo\n _ = send through \"Tarefa B completada\" tubo\n primeiro = receive from tubo\n segundo = receive from tubo\n echo primeiro \"|\" segundo\n```\n\n### 4. Axentes sumidoiro (*fire-and-forget* `agent sink`), caixas de correo globais (`agent ::`) e probas deterministas (`with agent`)\nCando precisas auditoría asíncrona, métricas ou trazabilidade transversal sen ensuciar todas as sinaturas nin acumular respostas sen ler nunha cola de saída, Ridgun ofrece **axentes sumidoiro (`agent sink`)** e **caixas de correo globais de nivel superior (`agent ::`)** (`agent dropped`, `agent flush`, `agent state` e `with agent audit_log = espia do ...`), ademais de descritores `Channel` e `Agent` compartibles entre múltiples traballadores.\n\nE agora, abrocha o cinto para o **Capítulo 08**, o gran final: **tensores con dimensións verificadas en compilación, variables de ámbito (`context`/`shared`), rexións de memoria deterministas, acceso directo a hardware MMIO e programación de sistemas UNIX/POSIX**.\n"}, {"num":"08","slug":"08-tensores-rmm-rexions-e-sistemas","file":"08-tensores-rmm-rexions-e-sistemas.md","title":"Capítulo 08: Tensores, Memoria Compilada RMM (`RMM1`/`RMM2`), Rexións e Sistemas","short_title":"Tensores, Memoria Compilada RMM (`RMM1`/`RMM2`), Rexións e Sistemas","markdown":"# Capítulo 08: Tensores, Memoria Compilada RMM (`RMM1`/`RMM2`), Rexións e Sistemas\n\nAta aquí vimos o lado máis amable, elegante e matemático de Ridgun: funcións puras, tipos alxébricos, *pattern matching*, árbores de decisión con `|` / `|.` e grafos paralelos `||` con `->` que aproveitan todos os núcleos do teu procesador sen pedirche nin un triste `Mutex`.\n\nChegados a este punto, o programador veterano de sistemas adoita erguer a man ao fondo da aula con medio sorriso e preguntar:\n\n> *\"Todo isto é precioso para calcular facturas ou compilar ASTs, pero que pasa cando preciso multiplicar matrices de millóns de flotantes a velocidade de vertixe? Que pasa coa memoria? Hai un colector de lixo (Garbage Collector) agochado debaixo da alfombra agardando o peor momento para conxelar o meu programa durante 200 milisegundos? E como falo co sistema operativo Unix de verdade?\"*\n\nExcelentes preguntas. Imos levantar o capó de Ridgun para ver como se las gasta cando toca baixar ao barro dos sistemas, o cálculo numérico e a memoria determinista.\n\n---\n\n## 8.1. O dilema da memoria: nin Garbage Collector nin pelexas co Borrow Checker (`RMM1` e `RMM2`)\n\nNo mundo das linguaxes modernas adoita haber dous bandos irreconciliables:\n\n1. **O bando do Garbage Collector (Java, Go, Python, JS, Haskell, OCaml):** ti pides memoria alegremente e, de cando en vez, un camión do lixo invisible detén o tráfico do teu programa para rastrexar que obxectos seguen vivos e cales non. É comodísimo... ata que estás facendo unha ferramenta de sistema ultrarrápida ou un servidor con latencias estritas e o camión decide aparcar en dobre fila.\n2. **O bando do control manual / Borrow Checker (C, C++, Rust):** non hai colector de lixo, pero a cambio tes que xestionar punteiros á man (xogándote un `Segmentation fault` ou un *use-after-free*) ou pasar media tarde negociando co *borrow checker* por que `'a` non vive tanto coma `'b`.\n\nRidgun resolve este dilema de raíz con **Ridgun Memory Management (`RMM`)**, un sistema determinista de dúas políticas complementarias (**`RMM1`** e **`RMM2`**) combinado con **rexións léxicas (`with region`)**:\n\n### Política `RMM1` — Xestión de Memoria Compilada (CMM Lineal e Condicional)\nCando o compilador coñece estaticamente o fluxo das variables locais dentro dunha función:\n- **CMM Lineal**: `ridgunc` calcula en tempo de compilación cal é a **última liña** na que se le cada variable local (`a`, `b`, ...) e insire xusto despois a súa liberación determinista (`rg_cmm_drop`), ou transfire a súa propiedade sen copias (`rg_cmm_move`) cando a variable se devolve como resultado.\n- **CMM Condicional (`ptr(a) != ptr(c)`)**: e se unha función como `escoller cond a b` devolve `a` ou `b` segundo unha condición en tempo de execución? `ridgunc` constrúe a táboa de devanceiros e emite unha garda de identidade de punteiros de **1 ciclo de CPU**: se `b` deixa de usarse mentres `c` segue viva, comproba `if ptr(b) != ptr(c) { free(b) }`. A rama non escollida libérase ao instante e a escollida sobrevive intacta.\n\n### Política `RMM2` — Conta por Asignación e Fervenza Estrutural\nE que ocorre cando metes estruturas dentro doutras estruturas (`xs = [a, b]`), reasignas variables mutables (`mut x = ...`) ou encadeas chamadas complexas? Aí entra **`RMM2`**, que goberna todo o ciclo de vida con catro regras matemáticas sen fisuras:\n\n1. **Os argumentos de entrada dunha función non se tocan (`+0` ao entrar, `-0` ao saír)**: pasar unha lista ou un rexistro a unha función xamais incrementa nin decrementa contadores en balde.\n2. **Toda asignación a unha variable local interna ou cela de contedor suma `+1`**:\n - `a = [10, 20]` $\\rightarrow$ o bloque `M1` nace con contador `1`.\n - `xs = [a, b]` $\\rightarrow$ a lista `xs` nace con `1`, e como garda referencias a `a` e `b`, ambos soben a `2`.\n - Se reasignas unha variable mutable (`mut x = ...` e logo `x = novo`), `RMM2` aplica primeiro **`+1`** ao valor novo antes de aplicar **`-1`** ao antigo.\n3. **Ao saír da función, as variables locais que NON se devolven reciben `-1`**: se o contador dunha estrutura chega a `0`, libérase a súa memoria no acto e **propaga `-1` en fervenza, un por un, a cada un dos seus fillos directos**.\n4. **O valor devolto pola función exímese en tránsito**: recibe o seu `-1` de variable local pero non se destrúe ao cruzar o `return`, de xeito que cando quen chamou fai `c = mina funcion ()`, ese `+1` da asignación déixao exactamente en `1`.\n\n```ridgun\nescoller Bool [Int] [Int] => [Int]\nescoller cond a b =\n | cond = a\n | otherwise = b\n\nexemplo rmm2 Bool => Int\nexemplo rmm2 cond =\n a = [10, 20, 30] -- M1 (rc = 1)\n b = [40, 50, 60] -- M2 (rc = 1)\n c = escoller cond a b -- `c` recibe +1; o non escollido morre aquí mesmo (rc -> 0)\n (contar a) + (contar c) -- Ao rematar, `a` e `c` reciben -1 e todo queda en 0 bloques vivos\n```\n\n---\n\n## 8.2. Rexións Léxicas con Cota (`with region`): Devasas de memoria en $O(1)$\n\nAdemais de `RMM1` e `RMM2`, cando estás procesando un paquete de rede externo, un ficheiro xigante ou un lote de datos non confiables e queres poñer un **teito estrito de memoria RAM**, envolves o cálculo nun bloque **`with region`**:\n\n```ridgun\nprocesar trama String => Result\nprocesar trama cru =\n with region 64_KB do\n limpas = map limpar lina (dividir linas cru)\n contar validos limpas\n```\n\n1. **Cota garantida en hardware/runtime**: se dentro do bloque se intenta reservar nin un só byte por riba de `64_KB` (`65.536` bytes), o runtime aborta limpamente o bloque, libera toda a area temporal en tempo constante $O(1)$ e devolve `Err OutOfMemory` sen tombar o proceso.\n2. **Liberación instantánea ao saír**: en canto remata o bloque `with region`, toda a memoria temporal da area desaparece de golpe sen deixar fragmentación.\n\n---\n\n## 8.3. Tensores Nativos, Contracción de Einstein (`@`) e Dimensións Simbólicas\n\nSe algunha vez programaches en Python con NumPy ou PyTorch, seguro que sufriches o clásico erro no minuto 41 de execución:\n\n```text\nRuntimeError: mat1 and mat2 shapes cannot be multiplied (128x64 and 32x128)\n```\n\nEn Ridgun, os **`Tensor`** son cidadáns nativos de primeira clase e as súas dimensións forman parte da sinatura de tipos:\n\n```ridgun\n-- Se entra (N × K) e (K × M), por forza sae (N × M)!\nmultiplicar matrices Tensor[N, K] Tensor[K, M] => Tensor[N, M]\nmultiplicar matrices a b = a_ik @ b_kj\n```\n\nFíxate en dous detalles espectaculares:\n1. **Verificación estática de dimensións (`Tensor[N, K]`)**: o eixe interno `K` da primeira matriz ten que coincidir co primeiro eixe `K` da segunda. Se intentas pasar dimensións incompatibles, `ridgunc check` deténte antes de executar nada.\n2. **Notación de subíndices de Einstein (`a_ik @ b_kj`) e Hadamard (`a_ij * b_j`)**:\n - Con **`@`**, os subíndices repetidos en todos os operandos (`k`) contráense (súmanse) e os únicos (`i`, `j`) sobreviven no tensor resultado.\n - Con **`+`, `-`, `*`, `/`**, os subíndices aliñan dimensións e fan *broadcasting* automático (`a_ij * b_j` multiplica cada fila `i` de `a` polo vector `b_j` sen necesidade de escribir `unsqueeze` nin `reshape`).\n3. **Tensores tipados de alto rendemento (`Tensor[U64 * N]`) e mutación *in-place* con `t_(i, j)`**:\n En sistemas criptográficos, simulacións físicas ou álxebra linear podes declarar tensores planos sobre un bloque contiguo de enteiros sen signo de 64 bits (`Tensor[U64 * 4]`, `Tensor[U64 * 8 * 16]`), operar en paralelo con `^` (XOR nativo de bloque) e ler ou escribir elementos en tempo $O(1)$ coa sintaxe de subíndice explícito `t_(i)` ou `t_(i, j)`:\n ```ridgun\n crear bloque estado => Tensor[U64 * 2 * 2]\n crear bloque estado =\n mut m = tensor fill [2, 2] (u64 0)\n m_(0, 1) = u64 0x6a09e667f3bcc908\n m\n ```\n\n---\n\n## 8.4. Acceso a Hardware (`hardware ::`) e Primitivas POSIX de Sistemas\n\nO propio compilador `ridgunc`, o xestor `stable` e utilidades de sistemas Unix están escritos en Ridgun. Para iso, a linguaxe inclúe bloques de rexistros mapeados en memoria (`hardware ::`) e primitivas POSIX directas:\n\n```ridgun\nhardware :: UART0 @ 0x1000_0000\n tx_data : Int @ 0x00\n status : Int @ 0x04\n```\n\nE para traballar co sistema operativo:\n\n| Primitiva | Sinatura | Que fai |\n|---|---|---|\n| `cli args` | `=> [String]` | Argumentos de liña de comandos (`argv`). |\n| `read file` / `write file` | `String => String` / `String String => ()` | Lectura e escritura íntegra de ficheiros en disco. |\n| `read bytes` / `write bytes` | `String => [Int]` / `String [Int] => ()` | Lectura e escritura binaria exacta byte a byte (`0..255`). |\n| `list dir` / `file lstat` | `String => [String]` / `String => ...` | Listaxe de directorios e inspección de metadatos POSIX (`lstat`). |\n| `sha256 file` / `sha256 str` / `sha256 raw` | `String => String` | Hash criptográfico SHA-256 nativo (en hexadecimal de 64 caracteres ou resumo binario cru de 32 bytes con `sha256 raw` / `sha256 bytes`). |\n| `sys exec` / `shell exec` | `... => Int` | Execución de procesos do sistema operativo. |\n| `sys getcwd` / `sys chdir` | `=> String` / `String => Int` | Consultar ou mudar o directorio de traballo. |\n| `sys getenv` / `sys setenv` | `String => String` / `String String => Int` | Variables de contorno do proceso. |\n| `sys getuid` / `sys getgid` | `=> Int` | UID e GID reais do proceso (`doas`, ferramentas de seguridade). |\n| `sys exit` | `Int => ()` | Rematar o proceso con código de saída. |\n\n---\n\n## 8.5. O Ecosistema de Bibliotecas Estándar (`ridgun-libs`)\n\nAdemais das primitivas do compilador, o ecosistema oficial de Ridgun inclúe **32 bibliotecas puras (`.rglib`)** escritas integramente en Ridgun que podes importar con `import <nome>` en calquera proxecto:\n\n| Categoría | Bibliotecas dispoñibles | Que achegan |\n|---|---|---|\n| **Coleccións, Texto e Datos** | `collections`, `set`, `string`, `regex`, `utf8`, `fmt`, `csv`, `json`, `toml`, `yaml`, `xml`, `html`, `markdown`, `base64`, `hex`, `compress`, `sql` | Mapas, conxuntos, expresións regulares, parsers/serializadores estruturados, codificación e motores de consultas. |\n| **Matemáticas, Tempo e Aleatoriedade** | `math`, `random`, `datetime`, `uuid`, `log`, `cli`, `bench`, `test` | Funcións matemáticas, PRNG determinista e de sistema, datas ISO-8601, UUIDs, marcos de probas e benchmarks con `clock_gettime`. |\n| **Sistemas, Rede e Web** | `fs`, `path`, `process`, `env`, `net`, `http`, `uri` | Sistema de ficheiros de alto nivel, manipulación de rutas, sub-procesos, sockets TCP/UDP e cliente/servidor HTTP. |\n| **Criptografía e Seguridade (`crypto`)** | `crypto` (`sha256`, `hmac`, `hkdf`, `pbkdf2`, `argon2id`, `chacha20`, `aes`, `x25519`, `ed25519`, `csprng`) | Suite criptográfica verificada con vectores oficiais RFC/NIST, implementada sobre `U64`, `Tensor[U64 * ...]` e `sha256 raw` con optimización de inlining de tuplas sen reservas no heap. |\n\n---\n\n## 8.6. Caso de Estudo Real: Construíndo `rg-wc`, unha utilidade Unix paralela\n\nImos unir todo o aprendido desde o Capítulo 01 ata aquí nun programa real de sistemas: unha versión paralela da ferramenta Unix `wc` (contador de liñas e bytes para múltiples ficheiros) escrita en Ridgun puro:\n\n```ridgun\n-- 1. Modelamos as métricas dun ficheiro cun rexistro ADT (Capítulo 06)\ntype Metricas = ficheiro: String, linas: Int, bytes: Int\n\n-- 2. Contamos saltos de liña (código ASCII 10) con recursión de cola TCO (Capítulo 04)\nfn :: contar saltos String Int Int Int => Int\n contar saltos texto pos fin acum =\n | pos >= fin = acum\n | (str char at texto pos) == 10 = contar saltos texto (pos + 1) fin (acum + 1)\n | otherwise = contar saltos texto (pos + 1) fin acum\n\nfn :: numero de linas String => Int\n numero de linas texto = contar saltos texto 0 (str len texto) 0\n\n-- 3. Analizamos un ficheiro e devolvemos a súa estrutura Metricas\nfn :: analizar ficheiro String => Metricas\n analizar ficheiro ruta =\n contido = read file ruta\n || numero de linas contido -> n_linas\n || str len contido -> n_bytes ||.\n Metricas ruta n_linas n_bytes\n\n-- 4. Formatamos cada fila e sumamos totais con `fold` e `||` (Capítulo 07)\nfn :: formatar metrica Metricas => String\n formatar metrica m = \"{linas m}\\t{bytes m}\\t{ficheiro m}\"\n\nfn :: sumar linas Int Metricas => Int\n sumar linas acc m = acc + (linas m)\n\nfn :: sumar bytes Int Metricas => Int\n sumar bytes acc m = acc + (bytes m)\n\nfn :: xerar informe [String] => String\n xerar informe rutas =\n metricas = ||map analizar ficheiro rutas\n || map formatar metrica metricas -> filas\n || fold sumar linas 0 metricas -> total_l\n || fold sumar bytes 0 metricas -> total_b ||.\n \"{filas}\\nTotal: {total_l} liñas, {total_b} bytes\"\n\nfn :: main => ()\n main =\n echo (xerar informe [\"README.md\", \"AGENTS.md\"])\n```\n\nPárate un segundo a mirar esas ~40 liñas de código:\n- Non hai nin un só `null`, nin unha excepción descontrolada, nin un `Mutex`.\n- `contar saltos` compílase a un `loop` nativo con 0 bytes de pila grazas ao TCO automático.\n- `||map analizar ficheiro rutas` procesa os ficheiros en paralelo en todos os núcleos da túa CPU, e o bloque `||` calcula simultaneamente as filas formatadas, o total de liñas e o total de bytes.\n- A memoria temporal libérase deterministicamente mediante **`RMM1`/`RMM2`**.\n- E cando executas `ridgunc build rg_wc.rg -o rg-wc`, obtés un binario nativo autónomo de ~350 KB listo para copiar a `/usr/local/bin`.\n\n---\n\n## 8.7. Folla de Ruta Final: As 10 Regras de Ouro do Programador Ridgun\n\n1. **Escribe sempre primeiro o contrato (`=>`) e despois as ecuacións (`=`):** pensar nos tipos primeiro fai que o compilador traballe para ti.\n2. **Deixa que `stable format` maquete as túas táboas:** escribe cun espazo e deixa que a ferramenta aliñe columnas, `=>`, `=` e `->`.\n3. **Lembra a regra universal do `=`:** detrás de calquera `=` podes abrir un bloque secuencial, unha árbore de gardas `|` ou un grafo paralelo `||`.\n4. **Usa `|.` e `||.` só cando peches un bloque aniñado para volver a un nivel superior:** mantén as árbores de decisión profundas compactas e sen ambigüidade.\n5. **Inmutable por defecto (`x = ...`), mutable explícito (`mut x = ...`):** usa `mut` unicamente cando un acumulador local o pida de verdade.\n6. **Usa operadores booleanos claros en minúsculas (`and`, `or`, `xor`, `not`) e pecha sempre as túas gardas con `| otherwise =`.**\n7. **Modela o teu dominio con ADTs (`type`):** usa rexistros con `with campo: valor` e tipos suma (`Option`, `Result`) en lugar de `null`.\n8. **Paraleliza declarando dependencias con `|| expr -> var` e `||map` / `||filter`:** deixa que o compilador verifique que non hai ciclos e reparta o traballo entre os núcleos.\n9. **Ponlle dimensións simbólicas aos teus Tensores (`Tensor[N, K]`) e subíndices ás túas contraccións (`a_ik @ b_kj`).**\n10. **Confía en `RMM` (`RMM1`/`RMM2`), acouta entradas externas con `with region` e verifica sempre con `stable check` e `stable test`.**\n\nFeliz viaxe programando en Ridgun!\n"}]; |
| 416 | const UI_STRINGS = {"copy_label":"Copiar","copied_label":"Copiado!","prev_label":"← Capítulo anterior","next_label":"Seguinte capítulo →","search_hits_label":"Resultados para","search_empty_label":"Non se atoparon coincidencias para"}; |
| 417 | let currentIdx = 0; |
| 418 | |
| 419 | function escapeHtml(s) { |
| 420 | return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); |
| 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(/(::|=>|->|<-|\|\|\.|\|\||\|\.)/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, "→"); |
| 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> |