Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capítulo 01: O teu primeiro contacto con Ridgun (Sen cascos, sen chaves e sen puntos e coma) |
| 2 | |
| 3 | Vale, xa te convencín no Capítulo 00 de que non estamos tolos por crear Ridgun. Agora seguramente esteas pensando: |
| 4 | |
| 5 | > *"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?"* |
| 6 | |
| 7 | Imos aló, mans á obra! |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## 1.1. "Ola, mundo!", pero con substancia |
| 12 | |
| 13 | Na 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. |
| 14 | |
| 15 | Crea un ficheiro chamado `ola.rg` e escribe isto: |
| 16 | |
| 17 | ```ridgun |
| 18 | saudo String => String |
| 19 | saudo "" = "Ola, viaxeiro misterioso! (Non me dixeches o teu nome)" |
| 20 | saudo "Root" = "Home, Root! Coidado co que borras hoxe." |
| 21 | saudo nome = "Ola, {nome}! Benvido a Ridgun." |
| 22 | |
| 23 | main => () |
| 24 | main = |
| 25 | echo (saudo "Ada") |
| 26 | echo (saudo "Root") |
| 27 | echo (saudo "") |
| 28 | ``` |
| 29 | |
| 30 | Para executalo agora mesmo: |
| 31 | |
| 32 | ```bash |
| 33 | ridgunc run ola.rg |
| 34 | ``` |
| 35 | |
| 36 | E verás na túa terminal: |
| 37 | |
| 38 | ```text |
| 39 | Ola, Ada! Benvido a Ridgun. |
| 40 | Home, Root! Coidado co que borras hoxe. |
| 41 | Ola, viaxeiro misterioso! (Non me dixeches o teu nome) |
| 42 | ``` |
| 43 | |
| 44 | Para un momento e mira o bloque de `saudo` arriba. Notas o que **NON** hai? |
| 45 | - **Non hai `function`, nin `def`, nin `pub fn`** (se queres poñer `fn ::` diante da sinatura podes facelo, pero é opcional). |
| 46 | - **Non hai parénteses `(String nome)` nin chaves `{ ... }`** lixando a pantalla. |
| 47 | - **Non hai `if (nome == "") ... else if (nome == "Root") ... else ...`**. |
| 48 | - **Non hai `return`**. |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## 1.2. Como le o teu cerebro un ficheiro `.rg` |
| 53 | |
| 54 | Imos destripar esas catro liñas de `saudo`, porque aquí xa está o 50% da filosofía de Ridgun: |
| 55 | |
| 56 | ```ridgun |
| 57 | saudo String => String |
| 58 | saudo "" = "Ola, viaxeiro misterioso! (Non me dixeches o teu nome)" |
| 59 | saudo "Root" = "Home, Root! Coidado co que borras hoxe." |
| 60 | saudo nome = "Ola, {nome}! Benvido a Ridgun." |
| 61 | ``` |
| 62 | |
| 63 | 1. **A primeira liña é o contrato (`saudo String => String`)**: |
| 64 | 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. |
| 65 | 2. **As liñas de abaixo son os casos (de arriba abaixo)**: |
| 66 | - Pasáchesme a cadea baleira `""`? Devolvo a primeira frase. |
| 67 | - Pasáchesme exactamente `"Root"`? Devolvo a segunda frase. |
| 68 | - Pasáchesme calquera outra cousa? Gárdoa na variable `nome` e métoa dentro do texto usando `{nome}`. |
| 69 | 3. **E que é `main => ()`?**: |
| 70 | 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`. |
| 71 | |
| 72 | --- |
| 73 | |
| 74 | ## 1.3. "Agarda... teño que aliñar eses espazos á man coma un monxe copista?" |
| 75 | |
| 76 | Nin de broma! Se tiveses que darlle á barra espazadora contando columnas a ollo, abandonarías Ridgun aos dez minutos (e con razón). |
| 77 | |
| 78 | Ti escribes o código axiña, cun só espazo entre cada palabra: |
| 79 | |
| 80 | ```ridgun |
| 81 | saudo String => String |
| 82 | saudo "" = "Ola, viaxeiro misterioso!" |
| 83 | saudo nome = "Ola, {nome}!" |
| 84 | ``` |
| 85 | |
| 86 | E 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. |
| 87 | |
| 88 | --- |
| 89 | |
| 90 | ## 1.4. De script rápido a binario de combate nun comando |
| 91 | |
| 92 | Cando 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. |
| 93 | |
| 94 | Executas: |
| 95 | |
| 96 | ```bash |
| 97 | ridgunc build ola.rg -o ola |
| 98 | ``` |
| 99 | |
| 100 | E 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. |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## 1.5. A chuleta honesta: De onde vén cada idea de Ridgun? |
| 105 | |
| 106 | Ridgun non pretende reinventar a roda cadrada; toma prestado sen complexos o mellor de catro tradicións: |
| 107 | |
| 108 | | De quen | Que toma Ridgun | Que deixa fóra (pola túa saúde mental) | |
| 109 | |---|---|---| |
| 110 | | **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. | |
| 111 | | **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. | |
| 112 | | **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. | |
| 113 | | **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. | |
| 114 | |
| 115 | --- |
| 116 | |
| 117 | ## 1.6. E o máis importante: Que fai Ridgun que NON ten ninguén máis? |
| 118 | |
| 119 | Recoñ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. |
| 120 | |
| 121 | Hai **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: |
| 122 | |
| 123 | ### 1. Bloques paralelos `||` con `->` que son grafos paralelos reais, non simple azucre sintáctico |
| 124 | Noutras linguaxes funcionais un bloque de variables locais só ordena expresións que logo se avalían nun único fío. |
| 125 | En Ridgun, **un bloque `||` con frechas `->` é un Grafo Acíclico Dirixido (DAG) de execución paralela** sen sequera precisar escribir palabras chave extra como `where`: |
| 126 | ```ridgun |
| 127 | informe anual Int => Int |
| 128 | informe anual id = ingresos - gastos |
| 129 | || calcular vendas id -> ingresos |
| 130 | || calcular custos id -> gastos |
| 131 | ``` |
| 132 | Ti 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`. |
| 133 | |
| 134 | ### 2. Modelo Nativo de Axentes (`agent`) de cero fricción con elección intelixente en compilación |
| 135 | Noutras 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`. |
| 136 | En Ridgun, **calquera función pura normal (`fn ::` ou `op ::`) convértese directamente nun axente autónomo —con ou sen estado illado— usando `agent`**: |
| 137 | ```ridgun |
| 138 | atender http (Sesion, String) => (Sesion, String) |
| 139 | ... |
| 140 | servidor = agent atender http sesion_inicial |
| 141 | send "GET /perfil" to servidor |
| 142 | resposta = receive from servidor |
| 143 | stop servidor |
| 144 | ``` |
| 145 | Mellor 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. |
| 146 | |
| 147 | ### 3. O código como táboa bidimensional onde a sinatura é a cabeceira das columnas |
| 148 | En 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. |
| 149 | En 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 `=>`: |
| 150 | ```ridgun |
| 151 | dividir Int Int => Option |
| 152 | dividir _ 0 = None |
| 153 | dividir a b = Some (a / b) |
| 154 | ``` |
| 155 | Ninguén máis trata a sinatura e as ecuacións como unha única matriz visual verificada polo compilador e maquetada automaticamente por `stable format`. |
| 156 | |
| 157 | ### 4. Operadores frase (*Mixfix* N-arios) tipados e aliñados (`op ::`) |
| 158 | Na maioría das linguaxes, ou te aguantas con `+`, `-`, `*` ou como moito defines un operador binario críptico como `<|>` que ninguén sabe que significa. |
| 159 | En Ridgun podes crear **operadores que son frases lexibles con tantos ocos tipados como queiras** (incluíndo palabras contextuais como `type` ou `with`): |
| 160 | ```ridgun |
| 161 | op :: Int limitado entre Int e Int => Int |
| 162 | x limitado entre min e max = |
| 163 | | x < min = min |
| 164 | | x > max = max |
| 165 | | otherwise = x |
| 166 | ``` |
| 167 | E 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. |
| 168 | |
| 169 | ### 5. Máscaras visuais sobre secuencias (`s(_ + _)` e `-> (+ _ _ +) ->`) |
| 170 | Algunha vez tiveches que escribir unha ensalada de índices ou abrir unha tupla con variables mudas só para quedar con dúas posicións concretas? |
| 171 | En Ridgun pásaslle directamente unha **máscara visual**: |
| 172 | - `datos(_ + _)`: descarta o primeiro (`_`), queda co segundo (`+`), descarta o terceiro (`_`). |
| 173 | - `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. |
| 174 | Cero aritmética de índices, cero erros *off-by-one*. |
| 175 | |
| 176 | ### 6. Polimorfismo de "cero ruído" (`A`..`Z`) gobernado polo teu dominio |
| 177 | En 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)`. |
| 178 | En Ridgun simplemente usas unha letra maiúscula solta: |
| 179 | ```ridgun |
| 180 | intercambiar (A, B) => (B, A) |
| 181 | intercambiar (x, y) = (y, x) |
| 182 | ``` |
| 183 | *"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. |
| 184 | |
| 185 | ### 7. Cero ficheiros de configuración estraños: `build.rg` é Ridgun puro |
| 186 | Esquece 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. |
| 187 | |
| 188 | ### 8. A "Santísima Trindade" que parecía imposible: Nivel Haskell + Memoria RMM sen GC nin Lifetimes + Binarios de ~350 KB |
| 189 | Ata hoxe tiñas que escoller: |
| 190 | - Se querías expresividade funcional pura (Haskell, OCaml, Elixir), pagabas cun colector de lixo e un runtime pesado. |
| 191 | - 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`. |
| 192 | |
| 193 | Ridgun 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. |
| 194 | |
| 195 | Agora si: coas cartas sobre a mesa, imos ao **Capítulo 02** para poñer a traballar a **`ridgunc`** e **`stable`**. |