Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capítulo 00: ¿Por qué necesito Ridgun? ¿Ah, sí? No lo sabía... ¡Cuéntame más! |
| 2 | |
| 3 | Seamos sinceros: cuando alguien te dice *"hemos creado un nuevo lenguaje de programación"*, tu primera reacción natural —y muy sana— es arquear una ceja y pensar: |
| 4 | |
| 5 | > *"¿Otro más? Pero si ya tengo Python para ir rápido, Rust o C para cuando necesito velocidad, y Bash para pegar cosas en la terminal. ¿Para qué demonios necesito yo aprender Ridgun?"* |
| 6 | |
| 7 | Es una pregunta excelente. Y la respuesta corta es: **porque te has acostumbrado a sufrir tres dolores diarios pensando que eran inevitables**. |
| 8 | |
| 9 | Vamos a ponerles nombre. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Dolor n.º 1: "El síndrome de los dos mundos" (Prototipar en uno, reescribir en otro) |
| 14 | |
| 15 | Seguro que has vivido esta película: |
| 16 | |
| 17 | 1. Empiezas una herramienta o un motor de cálculo en **Python**, **JavaScript** o **Ruby** porque quieres pensar en el problema, no en punteros ni en el compilador. |
| 18 | 2. El código queda corto y bonito... pero cuando le pasas un archivo de 500 MB o intentas usar los 16 núcleos de tu máquina, se arrastra. Y si quieres distribuir tu programa a un servidor limpio, tienes que empaquetar medio gigabyte de entorno virtual, intérprete y librerías dinámicas. |
| 19 | 3. Resignado, decides reescribirlo en **C**, **C++** o **Rust**. Ahora vuela y ocupa unos pocos cientos de kilobytes, pero tu código de 40 líneas se ha convertido en 300 líneas llenas de `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, llaves `{}` y puntos y coma `;`. |
| 20 | |
| 21 | **¿Qué cambia con Ridgun?** |
| 22 | En Ridgun escribes código **más corto y limpio que en Python**, pero cuando ejecutas `ridgunc build`, obtienes **un único binario nativo ELF de ~350 KB** sin intérprete, sin máquina virtual externa y sin recolector de basura. No hay fase de *"luego lo reescribimos en el lenguaje rápido"*: el código limpio donde piensas el problema **ya es** el binario nativo de producción. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Dolor n.º 2: "Leer código ajeno (o el tuyo de hace 6 meses) es jugar a ser detective" |
| 27 | |
| 28 | Mira cómo escribimos lógica condicional en casi todos los lenguajes mainstream: |
| 29 | |
| 30 | ```text |
| 31 | if (estado == ACTIVO && (rol == ADMIN || (rol == EDITOR && tienePermiso))) { |
| 32 | if (cuota > 0) { |
| 33 | return procesar(usuario); |
| 34 | } else { |
| 35 | return denegar("Sin cuota"); |
| 36 | } |
| 37 | } else { |
| 38 | return denegar("No autorizado"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | Nuestros ojos no evolucionaron para emparejar llaves anidadas ni para desentrañar árboles de `if / else` escalonados. Cuando los ingenieros, matemáticos o físicos quieren entender reglas complejas sin equivocarse, no escriben párrafos de `if / else`: **dibujan una tabla**. |
| 43 | |
| 44 | En Ridgun, **el código es esa tabla**: |
| 45 | |
| 46 | ```ridgun |
| 47 | autorizar String String Bool Int => String |
| 48 | autorizar "activo" "admin" _ cuota = |
| 49 | | cuota > 0 = "Procesando petición" |
| 50 | | otherwise = "Denegado: sin cuota" |
| 51 | autorizar "activo" "editor" True cuota = |
| 52 | | cuota > 0 = "Procesando petición" |
| 53 | | otherwise = "Denegado: sin cuota" |
| 54 | autorizar _ _ _ _ = "Denegado: no autorizado" |
| 55 | ``` |
| 56 | |
| 57 | Mira las columnas: |
| 58 | - Columna 1: el estado (`"activo"` o `_`). |
| 59 | - Columna 2: el rol (`"admin"`, `"editor"` o `_`). |
| 60 | - Columna 3: si tiene permiso (`True` o `_`). |
| 61 | - Columna 4: la `cuota`. |
| 62 | |
| 63 | No tienes que ejecutar mentalmente el programa línea por línea para saber qué pasa cuando entra un `"editor"` con permiso `False`: tus ojos bajan verticalmente por la tabla y lo ven en medio segundo. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Dolor n.º 3: "Mi ordenador tiene 16 núcleos, pero mi programa solo usa 1 (porque me da pánico tocar hilos)" |
| 68 | |
| 69 | Hoy hasta el teléfono más modesto tiene 8 núcleos, y cualquier servidor o portátil de trabajo tiene entre 12 y 32. Sin embargo, el 95% de los programas cotidianos siguen ejecutándose en un solo hilo secuencial. |
| 70 | |
| 71 | ¿Por qué? Porque en los lenguajes imperativos, paralelizar significa abrir la caja de Pandora: |
| 72 | - ¿Y si dos hilos modifican el mismo diccionario a la vez? (*Data race*). |
| 73 | - ¿Y si bloqueo el `Mutex` A y luego el B en un hilo, y el B y luego el A en otro? (*Deadlock*). |
| 74 | - ¿Y si contamino toda mi base de código con `async` y `await` de arriba abajo? |
| 75 | |
| 76 | En Ridgun, **toda función es pura y todo dato es inmutable por defecto**. Eso tiene una consecuencia mágica: **dos expresiones que no dependen una de la otra jamás pueden interferir entre sí**. |
| 77 | |
| 78 | Por eso, en Ridgun no tienes que gestionar hilos para aprovechar tu CPU. Solo declaras **qué depende de qué** con barras paralelas `||` y flechas `->`: |
| 79 | |
| 80 | ```ridgun |
| 81 | informe mensual Datos => Informe |
| 82 | informe mensual d = combinar metricas_red metricas_disco metricas_cpu |
| 83 | || analizar red d -> metricas_red |
| 84 | || analizar disco d -> metricas_disco |
| 85 | || analizar cpu d -> metricas_cpu |
| 86 | ``` |
| 87 | |
| 88 | Tú solo has escrito tres líneas limpias indicando de dónde sale cada dato. El compilador ve que las tres ramas son independientes y sabe que puede lanzarlas en paralelo sin que exista ni la más mínima posibilidad de un *data race* o un *deadlock*. |
| 89 | |
| 90 | ¿Y si necesitas agentes persistentes con buzón de mensajes y estado aislado (por ejemplo, para mantener miles de sesiones de clientes, trabajadores en segundo plano o conexiones de red)? En Ridgun no tienes que definir clases `Actor` ni usar `Mutex`: **cualquier función normal se convierte en un agente autónomo en una sola línea 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 | Además, el compilador realiza una **elección inteligente en tiempo de compilación**: analiza estáticamente la profundidad y el coste de cada función en `||`, `||map`, `||filter` y `agent` para ejecutar las funciones ligeras como pasos *green-thread* de sobrecarga cero (sin pagar el coste de calentar hilos del sistema operativo) y despachar automáticamente los cálculos pesados o recursivos a hilos reales del kernel en múltiples núcleos (`SYS_clone`). |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Dolor n.º 4: "El recolector de basura decide pausar el mundo justo en el peor momento" |
| 105 | |
| 106 | Los lenguajes funcionales clásicos (Haskell, OCaml, Erlang, Clojure) son maravillosos para razonar sobre el código, pero casi todos dependen de un **Recolector de Basura (*Garbage Collector*)** en tiempo de ejecución. Eso los descarta cuando necesitas: |
| 107 | - Latencias predecibles al microsegundo. |
| 108 | - Controlar exactamente cuánta memoria RAM puede consumir una operación antes de abortarla limpiamente. |
| 109 | - Escribir utilidades del sistema operativo (`ls`, `find`, `sh`, `doas`, `make`) o tocar registros de hardware (`MMIO`). |
| 110 | |
| 111 | Ridgun te da **toda la elegancia del mundo funcional puro, pero con Gestión de Memoria de Ridgun (`RMM`: `RMM1` estática y `RMM2` por conteo de asignaciones en cascada) más regiones léxicas con cuota exacta (`with region`)**: |
| 112 | |
| 113 | ```ridgun |
| 114 | parsear paquete String => Result |
| 115 | parsear paquete crudo = |
| 116 | with region 16_KB do |
| 117 | decodificar trama crudo |
| 118 | ``` |
| 119 | |
| 120 | Cada estructura temporal es liberada de forma determinista por el compilador (`RMM1` / `RMM2`) en el instante en que deja de usarse, y si un atacante te envía un paquete malicioso diseñado para explotar la memoria, al superar el byte 16.384 la región se corta al instante, libera su arena en $O(1)$ y te devuelve `Err OutOfMemory`. Sin caídas del proceso, sin pausas de GC y sin fugas de memoria. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## En resumen: ¿Cuándo brilla Ridgun? |
| 125 | |
| 126 | Ridgun está hecho para ti si alguna vez has querido: |
| 127 | |
| 128 | 1. **Escribir herramientas de sistemas, compiladores, analizadores o utilidades CLI** que arranquen en microsegundos, pesen unos pocos cientos de KB y no puedan romperse por punteros nulos ni efectos colaterales ocultos. |
| 129 | 2. **Construir sistemas concurrentes y servidores basados en el Modelo Nativo de Agentes (`agent`, `send ... to`, `receive from`, `stop`)**, donde cualquier función pura se convierte en un agente con o sin estado aislado y el compilador elige inteligentemente entre ejecución *green-thread* ultraligera e hilos nativos multinúcleo. |
| 130 | 3. **Programar algoritmos, pipelines de datos o álgebra de tensores** donde el compilador verifique por ti hasta las dimensiones de las matrices (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) antes de ejecutar nada. |
| 131 | 4. **Disfrutar leyendo tu propio código**: que cada archivo `.rg` parezca una página de un libro bien maquetado gracias al formateador tabular automático (`stable format`). |
| 132 | |
| 133 | ¿Te pica la curiosidad? Pasa al **Capítulo 01** y escribamos nuestro primer programa en menos de dos minutos. |