Skip to content
Ridgun
English

Ridgun

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

00-por-que-preciso-ridgun.md133 lines · 7.9 KBSourceRaw

Capítulo 00: Por que preciso Ridgun? Ah, si? Non o sabía... Cóntame máis!

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:

"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?"

É unha pregunta excelente. E a resposta curta é: porque te acostumaches a sufrir catro dores diarias pensando que eran inevitables.

Imos poñerlles nome.


Dor n.º 1: "A síndrome dos dous mundos" (Prototipar nun, reescribir noutro)

Seguro que xa viviches esta película:

  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.
  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.
  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 ;.

Que muda con Ridgun? 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.


Dor n.º 2: "Ler código alleo (ou o teu de hai 6 meses) é xogar a ser detective"

Mira como escribimos lóxica condicional en case todas as linguaxes convencionais:

if (estado == ACTIVO && (rol == ADMIN || (rol == EDITOR && tenPermiso))) {
    if (cota > 0) {
        return procesar(usuario);
    } else {
        return denegar("Sen cota");
    }
} else {
    return denegar("Non autorizado");
}

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.

En Ridgun, o código é esa táboa:

autorizar String   String   Bool  Int  => String
autorizar "activo" "admin"  _     cota =
  | cota > 0  = "Procesando petición"
  | otherwise = "Denegado: sen cota"
autorizar "activo" "editor" True  cota =
  | cota > 0  = "Procesando petición"
  | otherwise = "Denegado: sen cota"
autorizar _        _        _     _    =  "Denegado: non autorizado"

Mira as columnas:

  • Columna 1: o estado ("activo" ou _).
  • Columna 2: o rol ("admin", "editor" ou _).
  • Columna 3: se ten permiso (True ou _).
  • Columna 4: a cota.

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.


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)"

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.

Por que? Porque nas linguaxes imperativas, paralelizar significa abrir a caixa de Pandora:

  • E se dous fíos modifican o mesmo dicionario á vez? (Data race).
  • E se bloqueo o Mutex A e logo o B nun fío, e o B e logo o A noutro? (Deadlock).
  • E se contamino toda a miña base de código con async e await de arriba abaixo?

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.

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 ->:

informe mensual Datos => Informe
informe mensual d     =  combinar metricas_rede metricas_disco metricas_cpu
  || analizar rede d  -> metricas_rede
  || analizar disco d -> metricas_disco
  || analizar cpu d   -> metricas_cpu

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.

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:

contador = agent (sumar visita) 0
send 1 to contador
send 5 to contador
total = receive from contador
stop contador

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).


Dor n.º 4: "O colector de lixo decide pausar o mundo xusto no peor momento"

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:

  • Latencias predicibles ao microsegundo.
  • Controlar exactamente canta memoria RAM pode consumir unha operación antes de abortala limpamente.
  • Escribir utilidades do sistema operativo (ls, find, sh, doas, make) ou tocar rexistros de hardware (MMIO).

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):

analizar paquete String => Result
analizar paquete cru    =
  with region 16_KB do
    decodificar trama cru

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.


En resumo: Cando brilla Ridgun?

Ridgun está feito para ti se algunha vez quixeches:

  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.
  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.
  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.
  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).

Pícache a curiosidade? Pasa ao Capítulo 01 e escribamos o noso primeiro programa en menos de dous minutos.