Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capítulo 01: Tu primer contacto con Ridgun (Sin cascos, sin llaves y sin puntos y coma) |
| 2 | |
| 3 | Vale, ya te he convencido en el Capítulo 00 de que no estamos locos por crear Ridgun. Ahora seguramente estés pensando: |
| 4 | |
| 5 | > *"Muy bonito el discurso de las tablas y los 16 núcleos, pero enséñame código de verdad. ¿Cuánto tarda uno en escribir algo que funcione sin pelearse con el compilador?"* |
| 6 | |
| 7 | Venga, ¡al turrón! |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## 1.1. "¡Hola, mundo!", pero con chicha |
| 12 | |
| 13 | En la mayoría de los libros de programación te enseñan un `print("Hola mundo")` soso y pasan de página. Nosotros vamos a escribir directamente una función con lógica real para que veas cómo respira Ridgun desde el minuto uno. |
| 14 | |
| 15 | Crea un archivo llamado `hola.rg` y escribe esto: |
| 16 | |
| 17 | ```ridgun |
| 18 | saludo String => String |
| 19 | saludo "" = "¡Hola, viajero misterioso! (No me has dicho tu nombre)" |
| 20 | saludo "Root" = "¡Hombre, Root! Cuidado con lo que borras hoy." |
| 21 | saludo nombre = "¡Hola, {nombre}! Bienvenido a Ridgun." |
| 22 | |
| 23 | main => () |
| 24 | main = |
| 25 | echo (saludo "Ada") |
| 26 | echo (saludo "Root") |
| 27 | echo (saludo "") |
| 28 | ``` |
| 29 | |
| 30 | Para ejecutarlo ahora mismo: |
| 31 | |
| 32 | ```bash |
| 33 | ridgunc run hola.rg |
| 34 | ``` |
| 35 | |
| 36 | Y verás en tu terminal: |
| 37 | |
| 38 | ```text |
| 39 | ¡Hola, Ada! Bienvenido a Ridgun. |
| 40 | ¡Hombre, Root! Cuidado con lo que borras hoy. |
| 41 | ¡Hola, viajero misterioso! (No me has dicho tu nombre) |
| 42 | ``` |
| 43 | |
| 44 | Para un momento y mira el bloque de `saludo` arriba. ¿Notas lo que **NO** hay? |
| 45 | - **No hay `function`, ni `def`, ni `pub fn`** (si quieres poner `fn ::` delante de la firma puedes hacerlo, pero es opcional). |
| 46 | - **No hay paréntesis `(String nombre)` ni llaves `{ ... }`** ensuciando la pantalla. |
| 47 | - **No hay `if (nombre == "") ... else if (nombre == "Root") ... else ...`**. |
| 48 | - **No hay `return`**. |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## 1.2. Cómo lee tu cerebro un archivo `.rg` |
| 53 | |
| 54 | Vamos a destripar esas cuatro líneas de `saludo`, porque aquí ya está el 50% de la filosofía de Ridgun: |
| 55 | |
| 56 | ```ridgun |
| 57 | saludo String => String |
| 58 | saludo "" = "¡Hola, viajero misterioso! (No me has dicho tu nombre)" |
| 59 | saludo "Root" = "¡Hombre, Root! Cuidado con lo que borras hoy." |
| 60 | saludo nombre = "¡Hola, {nombre}! Bienvenido a Ridgun." |
| 61 | ``` |
| 62 | |
| 63 | 1. **La primera línea es el contrato (`saludo String => String`)**: |
| 64 | Dice: *"Me llamo `saludo`, recibo un `String` y te devuelvo (`=>`) otro `String`"*. Punto. Si alguien intenta llamarme pasándome el número `42`, el compilador le dará un toque en el hombro antes siquiera de ejecutar el programa. |
| 65 | 2. **Las líneas de abajo son los casos (de arriba abajo)**: |
| 66 | - ¿Me has pasado la cadena vacía `""`? Devuelvo la primera frase. |
| 67 | - ¿Me has pasado exactamente `"Root"`? Devuelvo la segunda frase. |
| 68 | - ¿Me has pasado cualquier otra cosa? La guardo en la variable `nombre` y la meto dentro del texto usando `{nombre}`. |
| 69 | 3. **¿Y qué es `main => ()`?**: |
| 70 | Como `main` no recibe argumentos, ponemos `=>` directamente después del nombre. Y devuelve `()` (el tipo vacío o *Unit*, para los amigos), porque su único trabajo es sacar cosas por pantalla con `echo`. |
| 71 | |
| 72 | --- |
| 73 | |
| 74 | ## 1.3. "Espera... ¿tengo que alinear esos espacios a mano como un monje copista?" |
| 75 | |
| 76 | ¡Ni de broma! Si tuvieras que darle a la barra espaciadora contando columnas a ojo, abandonarías Ridgun a los diez minutos (y con razón). |
| 77 | |
| 78 | Tú escribes el código deprisa, con un solo espacio entre cada palabra: |
| 79 | |
| 80 | ```ridgun |
| 81 | saludo String => String |
| 82 | saludo "" = "¡Hola, viajero misterioso!" |
| 83 | saludo nombre = "¡Hola, {nombre}!" |
| 84 | ``` |
| 85 | |
| 86 | Y cuando ejecutas **`stable format`** (o pulsas `TAB` / guardas el archivo en Emacs con `ridgun-mode`), el formateador canónico coloca cada parámetro debajo de su tipo y alinea todos los `=` con precisión milimétrica. Tú escribes rápido; la máquina maqueta la tabla. |
| 87 | |
| 88 | --- |
| 89 | |
| 90 | ## 1.4. De script rápido a binario de combate en un comando |
| 91 | |
| 92 | Cuando haces `ridgunc run hola.rg`, estás compilando y lanzando el programa al vuelo. Pero imagina que ya has terminado tu herramienta y quieres llevártela a un servidor de producción o compartirla con un compañero que no tiene instalado Ridgun. |
| 93 | |
| 94 | Ejecutas: |
| 95 | |
| 96 | ```bash |
| 97 | ridgunc build hola.rg -o hola |
| 98 | ``` |
| 99 | |
| 100 | Y ya tienes `./hola`: un **ejecutable nativo ELF de Linux x86_64**, limpio, sin código muerto (*Tree-Shaking*), con optimización de enlace completo (*LTO*) y sin depender de ningún intérprete externo. |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## 1.5. La chuleta honesta: ¿De dónde viene cada idea de Ridgun? |
| 105 | |
| 106 | Ridgun no pretende reinventar la rueda cuadrada; toma prestado sin complejos lo mejor de cuatro tradiciones: |
| 107 | |
| 108 | | De quién | Qué toma Ridgun | Qué deja fuera (por tu salud mental) | |
| 109 | |---|---|---| |
| 110 | | **Haskell / ML** | Funciones puras, *pattern matching* por casos, tipos algebraicos (`Option`, `Result`) y polimorfismo (`A`..`Z`). | Las mónadas abstractas, los 40 `LANGUAGE pragmas` y el recolector de basura. | |
| 111 | | **J / APL** | La obsesión por la forma tabular del código, tensores nativos y máscaras de secuencias (`s(_ + _)`). | Los símbolos jeroglíficos imposibles de escribir en un teclado normal. | |
| 112 | | **Rust / C** | Binarios ELF nativos pequeños, cero GC, control de memoria (`with region`) y llamadas al sistema POSIX. | La pelea constante con el *borrow checker*, los *lifetimes* `'a` y la verbosidad sintáctica. | |
| 113 | | **Erlang / Go** | Agentes aislados (`agent`, `send ... to`, `receive from`) y concurrencia por canales (`new bounded channel`). | Los punteros `nil` y las mutaciones compartidas por descuido. | |
| 114 | |
| 115 | --- |
| 116 | |
| 117 | ## 1.6. Y lo más importante: ¿Qué hace Ridgun que NO tiene nadie más? |
| 118 | |
| 119 | Reconocer a los clásicos es de buena educación, pero si Ridgun fuera solo un "recopilatorio de grandes éxitos" de otros lenguajes, no estarías leyendo este libro. |
| 120 | |
| 121 | Hay **ocho cosas** que hemos diseñado e integrado en Ridgun que no vas a encontrar juntas —y muchas de ellas, ni siquiera por separado— en ningún otro lenguaje actual: |
| 122 | |
| 123 | ### 1. Bloques paralelos `||` con `->` que son grafos paralelos reales, no simple azúcar sintáctico |
| 124 | En otros lenguajes funcionales un bloque de variables locales solo ordena expresiones que luego se evalúan en un único hilo. |
| 125 | En Ridgun, **un bloque `||` con flechas `->` es un Grafo Acíclico Dirigido (DAG) de ejecución paralela** sin siquiera necesitar escribir palabras clave extra como `where`: |
| 126 | ```ridgun |
| 127 | informe anual Int => Int |
| 128 | informe anual id = ingresos - gastos |
| 129 | || calcular ventas id -> ingresos |
| 130 | || calcular costes id -> gastos |
| 131 | ``` |
| 132 | Tú dibujas las flechas de flujo (`->`). El compilador analiza quién depende de quién, **verifica estáticamente que no hayas creado ningún ciclo infinito**, y lanza las ramas independientes en paralelo sin que escribas ni un solo `async`, `await`, `Future` ni `Mutex`. |
| 133 | |
| 134 | ### 2. Modelo Nativo de Agentes (`agent`) de cero fricción con elección inteligente en compilación |
| 135 | En otros lenguajes, programar con actores o agentes te obliga a crear clases especiales, implementar *traits*, gestionar *locks* o infectar tus funciones con `async`. |
| 136 | En Ridgun, **cualquier función pura normal (`fn ::` u `op ::`) se convierte directamente en un agente autónomo —con o sin estado aislado— 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 | respuesta = receive from servidor |
| 143 | stop servidor |
| 144 | ``` |
| 145 | Mejor aún: el compilador **analiza en tiempo de compilación la profundidad y el coste computacional de la función** tanto en `agent` como en `||` y `||map`. Si la función es ligera, la ejecuta como un *green thread* instantáneo sin pagar el coste de calentar hilos del sistema operativo; si es un cálculo pesado o recursivo profundo, la despacha automáticamente en paralelo sobre hilos reales del kernel (`SYS_clone`) repartidos por los núcleos de tu CPU. |
| 146 | |
| 147 | ### 3. El código como tabla bidimensional donde la firma es la cabecera de las columnas |
| 148 | En cualquier otro lenguaje, la firma de tipos va por libre en una línea y el cuerpo de la función va debajo como un párrafo de texto normal. |
| 149 | En Ridgun, **la firma de tipos es literalmente la cabecera de una tabla**, y cada caso debajo alinea cada patrón justo en la vertical de su tipo y cada `=` bajo el `=>`: |
| 150 | ```ridgun |
| 151 | dividir Int Int => Option |
| 152 | dividir _ 0 = None |
| 153 | dividir a b = Some (a / b) |
| 154 | ``` |
| 155 | Nadie más trata la firma y las ecuaciones como una única matriz visual verificada por el compilador y maquetada automáticamente por `stable format`. |
| 156 | |
| 157 | ### 4. Operadores frase (*Mixfix* N-arios) tipados y alineados (`op ::`) |
| 158 | En la mayoría de los lenguajes, o te aguantas con `+`, `-`, `*` o como mucho defines un operador binario críptico como `<|>` que nadie sabe qué significa. |
| 159 | En Ridgun puedes crear **operadores que son frases legibles con tantos huecos tipados como quieras** (incluyendo palabras contextuales como `type` o `with`): |
| 160 | ```ridgun |
| 161 | op :: Int limitado entre Int y Int => Int |
| 162 | x limitado entre min y max = |
| 163 | | x < min = min |
| 164 | | x > max = max |
| 165 | | otherwise = x |
| 166 | ``` |
| 167 | Y luego en tu código escribes `velocidad limitado entre 0 y 120`. Se lee como castellano (o inglés), pero está 100% tipado y compilado a código nativo. |
| 168 | |
| 169 | ### 5. Máscaras visuales sobre secuencias (`s(_ + _)` y `-> (+ _ _ +) ->`) |
| 170 | ¿Alguna vez has tenido que escribir una ensalada de índices `filter(\(i, x) -> i % 2 == 0)` o abrir una tupla con variables mudas solo para quedarte con dos posiciones concretas? |
| 171 | En Ridgun le pasas directamente una **máscara visual**: |
| 172 | - `datos(_ + _)`: descarta el primero (`_`), se queda el segundo (`+`), descarta el tercero (`_`). |
| 173 | - `parsear paquete "v0.9.9" -> (+ _ _ +) -> combinar codigo -> resumen`: filtra una tupla de 4 elementos en plena tubería y pasa el 1.º y el 4.º directamente como los 2 argumentos de la siguiente función. |
| 174 | Cero aritmética de índices, cero errores *off-by-one*. |
| 175 | |
| 176 | ### 6. Polimorfismo de "cero ruido" (`A`..`Z`) gobernado por tu dominio |
| 177 | En Rust, TypeScript o Java, cuando una función es genérica tienes que llenar la línea de paréntesis angulares: `fn intercambiar<A, B>(par: (A, B)) -> (B, A)`. |
| 178 | En Ridgun simplemente usas una letra mayúscula suelta: |
| 179 | ```ridgun |
| 180 | intercambiar (A, B) => (B, A) |
| 181 | intercambiar (x, y) = (y, x) |
| 182 | ``` |
| 183 | *"¿Y si en mi programa quiero tener un tipo real que se llame `type A`?"* — Ningún problema: en Ridgun **tus declaraciones `type` mandan**. Si no existe `type A`, `A` es genérico; en el instante en que defines `type A = ...`, pasa a ser tu tipo concreto en todo el módulo. |
| 184 | |
| 185 | ### 7. Cero archivos de configuración extraños: `build.rg` es Ridgun puro |
| 186 | Olvídate de aprender TOML para Cargo, JSON para npm, XML para Maven o la sintaxis arcana de `Makefile` con tabuladores invisibles. En Ridgun, la receta que construye tu proyecto (`build.rg`) **es un archivo `.rg` más**, con las mismas funciones puras y el mismo *pattern matching* que el resto de tu código. |
| 187 | |
| 188 | ### 8. La "Santísima Trinidad" que parecía imposible: Nivel Haskell + Memoria RMM sin GC ni Lifetimes + Binarios de ~350 KB |
| 189 | Hasta hoy tenías que elegir: |
| 190 | - Si querías expresividad funcional pura (Haskell, OCaml, Elixir), pagabas con un recolector de basura y un runtime pesado. |
| 191 | - Si querías binarios diminutos de sistema sin recolector de basura (C, Rust, Zig), pagabas gestionando punteros o peleándote con el *borrow checker* y escribiendo código imperativo lleno de `let mut`. |
| 192 | |
| 193 | Ridgun rompe ese dilema combinando **Ridgun Memory Management (`RMM`: `RMM1` estática y `RMM2` por conteo de asignaciones en cascada)**, **regiones léxicas (`with region`)** que liberan arenas enteras en $O(1)$, **recursión de cola transformada en bucles nativos (`loop`)**, y **poda de grafo de llamadas (*Tree-Shaking*)** desde `main`. El resultado: escribes código declarativo de altísimo nivel y obtienes un ejecutable ELF autónomo de **~350 KB** capaz de competir de tú a tú con herramientas escritas en C. |
| 194 | |
| 195 | Ahora sí: con las cartas sobre la mesa, vamos al **Capítulo 02** para poner a trabajar a **`ridgunc`** y **`stable`**. |
| 196 |