Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capítulo 00: Por que preciso Ridgun? Ah, si? Non o sabía... Cóntame máis! |
| 2 | |
| 3 | Sexamos 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: |
| 4 | |
| 5 | > *"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?"* |
| 6 | |
| 7 | É unha pregunta excelente. E a resposta curta é: **porque te acostumaches a sufrir catro dores diarias pensando que eran inevitables**. |
| 8 | |
| 9 | Imos poñerlles nome. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Dor n.º 1: "A síndrome dos dous mundos" (Prototipar nun, reescribir noutro) |
| 14 | |
| 15 | Seguro que xa viviches esta película: |
| 16 | |
| 17 | 1. 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. |
| 18 | 2. 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. |
| 19 | 3. 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 `;`. |
| 20 | |
| 21 | **Que muda con Ridgun?** |
| 22 | En 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. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Dor n.º 2: "Ler código alleo (ou o teu de hai 6 meses) é xogar a ser detective" |
| 27 | |
| 28 | Mira como escribimos lóxica condicional en case todas as linguaxes convencionais: |
| 29 | |
| 30 | ```text |
| 31 | if (estado == ACTIVO && (rol == ADMIN || (rol == EDITOR && tenPermiso))) { |
| 32 | if (cota > 0) { |
| 33 | return procesar(usuario); |
| 34 | } else { |
| 35 | return denegar("Sen cota"); |
| 36 | } |
| 37 | } else { |
| 38 | return denegar("Non autorizado"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | Os 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**. |
| 43 | |
| 44 | En Ridgun, **o código é esa táboa**: |
| 45 | |
| 46 | ```ridgun |
| 47 | autorizar String String Bool Int => String |
| 48 | autorizar "activo" "admin" _ cota = |
| 49 | | cota > 0 = "Procesando petición" |
| 50 | | otherwise = "Denegado: sen cota" |
| 51 | autorizar "activo" "editor" True cota = |
| 52 | | cota > 0 = "Procesando petición" |
| 53 | | otherwise = "Denegado: sen cota" |
| 54 | autorizar _ _ _ _ = "Denegado: non autorizado" |
| 55 | ``` |
| 56 | |
| 57 | Mira as columnas: |
| 58 | - Columna 1: o estado (`"activo"` ou `_`). |
| 59 | - Columna 2: o rol (`"admin"`, `"editor"` ou `_`). |
| 60 | - Columna 3: se ten permiso (`True` ou `_`). |
| 61 | - Columna 4: a `cota`. |
| 62 | |
| 63 | Non 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. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## 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)" |
| 68 | |
| 69 | Hoxe 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. |
| 70 | |
| 71 | Por que? Porque nas linguaxes imperativas, paralelizar significa abrir a caixa de Pandora: |
| 72 | - E se dous fíos modifican o mesmo dicionario á vez? (*Data race*). |
| 73 | - E se bloqueo o `Mutex` A e logo o B nun fío, e o B e logo o A noutro? (*Deadlock*). |
| 74 | - E se contamino toda a miña base de código con `async` e `await` de arriba abaixo? |
| 75 | |
| 76 | En 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**. |
| 77 | |
| 78 | Por 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 `->`: |
| 79 | |
| 80 | ```ridgun |
| 81 | informe mensual Datos => Informe |
| 82 | informe mensual d = combinar metricas_rede metricas_disco metricas_cpu |
| 83 | || analizar rede d -> metricas_rede |
| 84 | || analizar disco d -> metricas_disco |
| 85 | || analizar cpu d -> metricas_cpu |
| 86 | ``` |
| 87 | |
| 88 | Ti 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*. |
| 89 | |
| 90 | E 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`**: |
| 91 | |
| 92 | ```ridgun |
| 93 | contador = agent (sumar visita) 0 |
| 94 | send 1 to contador |
| 95 | send 5 to contador |
| 96 | total = receive from contador |
| 97 | stop contador |
| 98 | ``` |
| 99 | |
| 100 | Ademais, 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`). |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Dor n.º 4: "O colector de lixo decide pausar o mundo xusto no peor momento" |
| 105 | |
| 106 | As 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: |
| 107 | - Latencias predicibles ao microsegundo. |
| 108 | - Controlar exactamente canta memoria RAM pode consumir unha operación antes de abortala limpamente. |
| 109 | - Escribir utilidades do sistema operativo (`ls`, `find`, `sh`, `doas`, `make`) ou tocar rexistros de hardware (`MMIO`). |
| 110 | |
| 111 | Ridgun 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`)**: |
| 112 | |
| 113 | ```ridgun |
| 114 | analizar paquete String => Result |
| 115 | analizar paquete cru = |
| 116 | with region 16_KB do |
| 117 | decodificar trama cru |
| 118 | ``` |
| 119 | |
| 120 | Cada 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. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## En resumo: Cando brilla Ridgun? |
| 125 | |
| 126 | Ridgun está feito para ti se algunha vez quixeches: |
| 127 | |
| 128 | 1. **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. |
| 129 | 2. **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. |
| 130 | 3. **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. |
| 131 | 4. **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`). |
| 132 | |
| 133 | Pícache a curiosidade? Pasa ao **Capítulo 01** e escribamos o noso primeiro programa en menos de dous minutos. |