Skip to content
Ridgun
English

Ridgun

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

08-tensors-rmm-regions-i-sistemes.md215 lines · 14.9 KBRaw
1# Capítol 08: Tensors, Memòria Compilada RMM (`RMM1`/`RMM2`), Regions i Sistemes
2
3Fins aquí hem vist el costat més amable, elegant i matemàtic de Ridgun: funcions pures, tipus algebraics, *pattern matching*, arbres de decisió amb `|` / `|.` i grafs paral·lels `||` amb `->` que aprofiten tots els nuclis del teu processador sense demanar-te ni un trist `Mutex`.
4
5Arribats a aquest punt, el programador veterà de sistemes sol aixecar la mà al fons de l'aula amb mig somriure i preguntar:
6
7> *"Tot això és preciós per calcular factures o compilar ASTs, però què passa quan necessito multiplicar matrius de milions de flotants a velocitat de vertigen? Què passa amb la memòria? Hi ha un recol·lector de brossa (Garbage Collector) amagat sota la catifa esperant el pitjor moment per congelar el meu programa durant 200 mil·lisegons? I com parlo amb el sistema operatiu Unix de debò?"*
8
9Excel·lents preguntes. Aixequem el capó de Ridgun per veure com se les gasta quan toca baixar al fang dels sistemes, el càlcul numèric i la memòria determinista.
10
11---
12
13## 8.1. El dilema de la memòria: ni Garbage Collector ni baralles amb el Borrow Checker (`RMM1` i `RMM2`)
14
15Al món dels llenguatges moderns sol haver-hi dos bàndols irreconciliables:
16
171. **El bàndol del Garbage Collector (Java, Go, Python, JS, Haskell, OCaml):** tu demanes memòria alegrement i, de tant en tant, un camió de la brossa invisible atura el trànsit del teu programa per rastrejar quins objectes continuen vius i quins no. És comodíssim... fins que estàs fent una eina de sistema ultraràpida o un servidor amb latències estrictes i el camió decideix aparcar en doble fila.
182. **El bàndol del control manual / Borrow Checker (C, C++, Rust):** no hi ha recol·lector de brossa, però a canvi has de gestionar punters a mà (jugant-te un `Segmentation fault` o un *use-after-free*) o passar-te mitja tarda negociant amb el *borrow checker* per què `'a` no viu tant com `'b`.
19
20Ridgun resol aquest dilema de soca-rel amb **Ridgun Memory Management (`RMM`)**, un sistema determinista de dues polítiques complementàries (**`RMM1`** i **`RMM2`**) combinat amb **regions lèxiques (`with region`)**:
21
22### Política `RMM1` — Gestió de Memòria Compilada (CMM Lineal i Condicional)
23Quan el compilador coneix estàticament el flux de les variables locals dins d'una funció:
24- **CMM Lineal**: `ridgunc` calcula en temps de compilació quina és l'**última línia** en què es llegeix cada variable local (`a`, `b`, ...) i insereix just després el seu alliberament determinista (`rg_cmm_drop`), o transfereix la seva propietat sense còpies (`rg_cmm_move`) quan la variable es retorna com a resultat.
25- **CMM Condicional (`ptr(a) != ptr(c)`)**: i si una funció com `triar cond a b` retorna `a` o `b` segons una condició en temps d'execució? `ridgunc` construeix la taula d'ancestres i emet una guarda d'identitat de punters d'**1 cicle de CPU**: si `b` deixa de fer-se servir mentre `c` continua viva, comprova `if ptr(b) != ptr(c) { free(b) }`. La branca no triada s'allibera a l'instant i la triada sobreviu intacta.
26
27### Política `RMM2` — Recompte per Assignació i Cascada Estructural
28I què passa quan fiques estructures dins d'altres estructures (`xs = [a, b]`), reassignes variables mutables (`mut x = ...`) o encadenes crides complexes? Aquí entra **`RMM2`**, que governa tot el cicle de vida amb quatre regles matemàtiques sense fissures:
29
301. **Els arguments d'entrada d'una funció no es toquen (`+0` en entrar, `-0` en sortir)**: passar una llista o un registre a una funció mai no incrementa ni decrementa comptadors en va.
312. **Tota assignació a una variable local interna o cel·la de contenidor suma `+1`**:
32 - `a = [10, 20]` $\rightarrow$ el bloc `M1` neix amb comptador `1`.
33 - `xs = [a, b]` $\rightarrow$ la llista `xs` neix amb `1`, i com que desa referències a `a` i `b`, tots dos pugen a `2`.
34 - Si reassignes una variable mutable (`mut x = ...` i després `x = nou`), `RMM2` aplica primer **`+1`** al valor nou abans d'aplicar **`-1`** a l'antic.
353. **En sortir de la funció, les variables locals que NO es retornen reben `-1`**: si el comptador d'una estructura arriba a `0`, s'allibera la seva memòria a l'instant i **propaga `-1` en cascada, un per un, a cadascun dels seus fills directes**.
364. **El valor retornat per la funció s'eximeix en trànsit**: rep el seu `-1` de variable local però no es destrueix en creuar el `return`, de manera que quan qui ha cridat fa `c = la meva funcio ()`, aquell `+1` de l'assignació el deixa exactament en `1`.
37
38```ridgun
39triar Bool [Int] [Int] => [Int]
40triar cond a b =
41 | cond = a
42 | otherwise = b
43
44exemple rmm2 Bool => Int
45exemple rmm2 cond =
46 a = [10, 20, 30] -- M1 (rc = 1)
47 b = [40, 50, 60] -- M2 (rc = 1)
48 c = triar cond a b -- `c` rep +1; el no triat mor aquí mateix (rc -> 0)
49 (comptar a) + (comptar c) -- En acabar, `a` i `c` reben -1 i tot queda en 0 blocs vius
50```
51
52---
53
54## 8.2. Regions Lèxiques amb Quota (`with region`): Tallafocs de memòria en $O(1)$
55
56A més d'`RMM1` i `RMM2`, quan estàs processant un paquet de xarxa extern, un fitxer gegant o un lot de dades no confiables i vols posar un **sostre estricte de memòria RAM**, envoltes el càlcul en un bloc **`with region`**:
57
58```ridgun
59processar trama String => Result
60processar trama cru =
61 with region 64_KB do
62 netes = map netejar linia (dividir linies cru)
63 comptar valids netes
64```
65
661. **Quota garantida en maquinari/runtime**: si dins del bloc s'intenta reservar ni un sol byte per sobre de `64_KB` (`65.536` bytes), el runtime avorta netament el bloc, allibera tota l'arena temporal en temps constant $O(1)$ i retorna `Err OutOfMemory` sense tombar el procés.
672. **Alliberament instantani en sortir**: tan bon punt acaba el bloc `with region`, tota la memòria temporal de l'arena desapareix de cop sense deixar fragmentació.
68
69---
70
71## 8.3. Tensors Natius, Contracció d'Einstein (`@`) i Dimensions Simbòliques
72
73Si alguna vegada has programat en Python amb NumPy o PyTorch, segur que has patit el clàssic error al minut 41 d'execució:
74
75```text
76RuntimeError: mat1 and mat2 shapes cannot be multiplied (128x64 and 32x128)
77```
78
79En Ridgun, els **`Tensor`** són ciutadans natius de primera classe i les seves dimensions formen part de la signatura de tipus:
80
81```ridgun
82-- Si entra (N × K) i (K × M), per nassos surt (N × M)!
83multiplicar matrius Tensor[N, K] Tensor[K, M] => Tensor[N, M]
84multiplicar matrius a b = a_ik @ b_kj
85```
86
87Fixa't en tres detalls espectaculars:
881. **Verificació estàtica de dimensions (`Tensor[N, K]`)**: l'eix intern `K` de la primera matriu ha de coincidir amb el primer eix `K` de la segona. Si intentes passar dimensions incompatibles, `ridgunc check` t'atura abans d'executar res.
892. **Notació de subíndexs d'Einstein (`a_ik @ b_kj`) i Hadamard (`a_ij * b_j`)**:
90 - Amb **`@`**, els subíndexs repetits en tots els operands (`k`) es contrauen (es sumen) i els únics (`i`, `j`) sobreviuen al tensor resultat.
91 - Amb **`+`, `-`, `*`, `/`**, els subíndexs alineen dimensions i fan *broadcasting* automàtic (`a_ij * b_j` multiplica cada fila `i` d'`a` pel vector `b_j` sense necessitat d'escriure `unsqueeze` ni `reshape`).
923. **Tensors tipats d'alt rendiment (`Tensor[U64 * N]`) i mutació *in-place* amb `t_(i, j)`**:
93 En sistemes criptogràfics, simulacions físiques o àlgebra lineal pots declarar tensors plans sobre un bloc contigu d'enters sense signe de 64 bits (`Tensor[U64 * 4]`, `Tensor[U64 * 8 * 16]`), operar en paral·lel amb `^` (XOR natiu de bloc) i llegir o escriure elements en temps $O(1)$ amb la sintaxi de subíndex explícit `t_(i)` o `t_(i, j)`:
94 ```ridgun
95 crear bloc estat => Tensor[U64 * 2 * 2]
96 crear bloc estat =
97 mut m = tensor fill [2, 2] (u64 0)
98 m_(0, 1) = u64 0x6a09e667f3bcc908
99 m
100 ```
101
102---
103
104## 8.4. Accés a Maquinari (`hardware ::`) i Primitives POSIX de Sistemes
105
106El mateix compilador `ridgunc`, el gestor `stable` i utilitats de sistemes Unix estan escrits en Ridgun. Per a això, el llenguatge inclou blocs de registres mapejats en memòria (`hardware ::`) i primitives POSIX directes:
107
108```ridgun
109hardware :: UART0 @ 0x1000_0000
110 tx_data : Int @ 0x00
111 status : Int @ 0x04
112```
113
114I per treballar amb el sistema operatiu:
115
116| Primitiva | Signatura | Què fa |
117|---|---|---|
118| `cli args` | `=> [String]` | Arguments de línia d'ordres (`argv`). |
119| `read file` / `write file` | `String => String` / `String String => ()` | Lectura i escriptura íntegra de fitxers en disc. |
120| `read bytes` / `write bytes` | `String => [Int]` / `String [Int] => ()` | Lectura i escriptura binària exacta byte a byte (`0..255`). |
121| `list dir` / `file lstat` | `String => [String]` / `String => ...` | Llistat de directoris i inspecció de metadades POSIX (`lstat`). |
122| `sha256 file` / `sha256 str` / `sha256 raw` | `String => String` | Resum criptogràfic SHA-256 natiu (en hexadecimal de 64 caràcters o digest binari cru de 32 bytes amb `sha256 raw` / `sha256 bytes`). |
123| `sys exec` / `shell exec` | `... => Int` | Execució de processos del sistema operatiu. |
124| `sys getcwd` / `sys chdir` | `=> String` / `String => Int` | Consultar o canviar el directori de treball. |
125| `sys getenv` / `sys setenv` | `String => String` / `String String => Int` | Variables d'entorn del procés. |
126| `sys getuid` / `sys getgid` | `=> Int` | UID i GID reals del procés (`doas`, eines de seguretat). |
127| `sys exit` | `Int => ()` | Acabar el procés amb codi de sortida. |
128
129---
130
131## 8.5. L'Ecosistema de Llibreries Estàndard (`ridgun-libs`)
132
133A més de les primitives del compilador, l'ecosistema oficial de Ridgun inclou **32 llibreries pures (`.rglib`)** escrites íntegrament en Ridgun que pots importar amb `import <nom>` en qualsevol projecte:
134
135| Categoria | Llibreries disponibles | Què aporten |
136|---|---|---|
137| **Col·leccions, Text i Dades** | `collections`, `set`, `string`, `regex`, `utf8`, `fmt`, `csv`, `json`, `toml`, `yaml`, `xml`, `html`, `markdown`, `base64`, `hex`, `compress`, `sql` | Mapes, conjunts, expressions regulars, parsers/serialitzadors estructurats, codificació i motors de consultes. |
138| **Matemàtiques, Temps i Aleatorietat** | `math`, `random`, `datetime`, `uuid`, `log`, `cli`, `bench`, `test` | Funcions matemàtiques, PRNG determinista i de sistema, dates ISO-8601, UUIDs, marcs de tests i benchmarks amb `clock_gettime`. |
139| **Sistemes, Xarxa i Web** | `fs`, `path`, `process`, `env`, `net`, `http`, `uri` | Sistema de fitxers d'alt nivell, manipulació de rutes, sub-processos, sockets TCP/UDP i client/servidor HTTP. |
140| **Criptografia i Seguretat (`crypto`)** | `crypto` (`sha256`, `hmac`, `hkdf`, `pbkdf2`, `argon2id`, `chacha20`, `aes`, `x25519`, `ed25519`, `csprng`) | Suite criptogràfica verificada amb vectors oficials RFC/NIST, implementada sobre `U64`, `Tensor[U64 * ...]` i `sha256 raw` amb optimització d'inlining de tuples sense reserves al heap. |
141
142---
143
144## 8.6. Cas d'Estudi Real: Construint `rg-wc`, una utilitat Unix paral·lela
145
146Unirem tot el que hem après des del Capítol 01 fins aquí en un programa real de sistemes: una versió paral·lela de l'eina Unix `wc` (comptador de línies i bytes per a múltiples fitxers) escrita en Ridgun pur:
147
148```ridgun
149-- 1. Modelem les mètriques d'un fitxer amb un registre ADT (Capítol 06)
150type Metriques = fitxer: String, linies: Int, bytes: Int
151
152-- 2. Comptem salts de línia (codi ASCII 10) amb recursió de cua TCO (Capítol 04)
153fn :: comptar salts String Int Int Int => Int
154 comptar salts text pos fi acum =
155 | pos >= fi = acum
156 | (str char at text pos) == 10 = comptar salts text (pos + 1) fi (acum + 1)
157 | otherwise = comptar salts text (pos + 1) fi acum
158
159fn :: nombre de linies String => Int
160 nombre de linies text = comptar salts text 0 (str len text) 0
161
162-- 3. Analitzem un fitxer i retornem la seva estructura Metriques
163fn :: analitzar fitxer String => Metriques
164 analitzar fitxer ruta =
165 contingut = read file ruta
166 || nombre de linies contingut -> n_linies
167 || str len contingut -> n_bytes ||.
168 Metriques ruta n_linies n_bytes
169
170-- 4. Formatem cada fila i sumem totals amb `fold` i `||` (Capítol 07)
171fn :: formatar metrica Metriques => String
172 formatar metrica m = "{linies m}\t{bytes m}\t{fitxer m}"
173
174fn :: sumar linies Int Metriques => Int
175 sumar linies acc m = acc + (linies m)
176
177fn :: sumar bytes Int Metriques => Int
178 sumar bytes acc m = acc + (bytes m)
179
180fn :: generar informe [String] => String
181 generar informe rutes =
182 metriques = ||map analitzar fitxer rutes
183 || map formatar metrica metriques -> files
184 || fold sumar linies 0 metriques -> total_l
185 || fold sumar bytes 0 metriques -> total_b ||.
186 "{files}\nTotal: {total_l} línies, {total_b} bytes"
187
188fn :: main => ()
189 main =
190 echo (generar informe ["README.md", "AGENTS.md"])
191```
192
193Atura't un segon a mirar aquestes ~40 línies de codi:
194- No hi ha ni un sol `null`, ni una excepció descontrolada, ni un `Mutex`.
195- `comptar salts` es compila a un `loop` natiu amb 0 bytes de pila gràcies al TCO automàtic.
196- `||map analitzar fitxer rutes` processa els fitxers en paral·lel en tots els nuclis de la teva CPU, i el bloc `||` calcula simultàniament les files formatades, el total de línies i el total de bytes.
197- La memòria temporal s'allibera determinísticament mitjançant **`RMM1`/`RMM2`**.
198- I quan executes `ridgunc build rg_wc.rg -o rg-wc`, obtens un binari natiu autònom de ~350 KB a punt per copiar a `/usr/local/bin`.
199
200---
201
202## 8.7. Full de Ruta Final: Les 10 Regles d'Or del Programador Ridgun
203
2041. **Escriu sempre primer el contracte (`=>`) i després les equacions (`=`):** pensar en els tipus primer fa que el compilador treballi per a tu.
2052. **Deixa que `stable format` maqueti les teves taules:** escriu amb un espai i deixa que l'eina alineï columnes, `=>`, `=` i `->`.
2063. **Recorda la regla universal de l'`=`:** darrere de qualsevol `=` pots obrir un bloc seqüencial, un arbre de guardes `|` o un graf paral·lel `||`.
2074. **Fes servir `|.` i `||.` només quan tanquis un bloc imbricat per tornar a un nivell superior:** mantén els arbres de decisió profunds compactes i sense ambigüitat.
2085. **Immutable per defecte (`x = ...`), mutable explícit (`mut x = ...`):** fes servir `mut` únicament quan un acumulador local ho demani de debò.
2096. **Fes servir operadors booleans clars en minúscules (`and`, `or`, `xor`, `not`) i tanca sempre les teves guardes amb `| otherwise =`.**
2107. **Modela el teu domini amb ADTs (`type`):** fes servir registres amb `with camp: valor` i tipus suma (`Option`, `Result`) en lloc de `null`.
2118. **Paral·lelitza declarant dependències amb `|| expr -> var` i `||map` / `||filter`:** deixa que el compilador verifiqui que no hi ha cicles i reparteixi la feina entre els nuclis.
2129. **Posa dimensions simbòliques als teus Tensors (`Tensor[N, K]`) i subíndexs a les teves contraccions (`a_ik @ b_kj`).**
21310. **Confia en `RMM` (`RMM1`/`RMM2`), afita entrades externes amb `with region` i verifica sempre amb `stable check` i `stable test`.**
214
215Bon viatge programant en Ridgun!