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 do Ridgun? Ah, é? Não sabia... Conte-me mais! |
| 2 | |
| 3 | Sejamos sinceros: quando alguém lhe diz *"criámos uma nova linguagem de programação"*, a sua primeira reação natural — e muito saudável — é arquear uma sobrancelha e pensar: |
| 4 | |
| 5 | > *"Mais uma? Mas se eu já tenho Python para ir rápido, Rust ou C para quando preciso de velocidade, e Bash para colar coisas no terminal. Para que raio preciso eu de aprender Ridgun?"* |
| 6 | |
| 7 | É uma excelente pergunta. E a resposta curta é: **porque se habituou a sofrer três dores diárias achando que eram inevitáveis**. |
| 8 | |
| 9 | Vamos dar-lhes um nome. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Dor n.º 1: "A síndrome dos dois mundos" (Prototipar num, reescrever noutro) |
| 14 | |
| 15 | De certeza que já viveu este filme: |
| 16 | |
| 17 | 1. Começa uma ferramenta ou um motor de cálculo em **Python**, **JavaScript** ou **Ruby** porque quer pensar no problema, não em ponteiros nem no compilador. |
| 18 | 2. O código fica curto e bonito... mas quando lhe passa um ficheiro de 500 MB ou tenta usar os 16 núcleos da sua máquina, arrasta-se. E se quiser distribuir o seu programa para um servidor limpo, tem de empacotar meio gigabyte de ambiente virtual, interpretador e bibliotecas dinâmicas. |
| 19 | 3. Resignado, decide reescrevê-lo em **C**, **C++** ou **Rust**. Agora voa e ocupa apenas algumas centenas de kilobytes, mas o seu código de 40 linhas transformou-se em 300 linhas cheias de `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, chavetas `{}` e pontos e vírgulas `;`. |
| 20 | |
| 21 | **O que muda com o Ridgun?** |
| 22 | Em Ridgun escreve código **mais curto e limpo do que em Python**, mas quando executa `ridgunc build`, obtém **um único binário nativo ELF de ~350 KB** sem interpretador, sem máquina virtual externa e sem coletor de lixo (*Garbage Collector*). Não há fase de *"depois reescrevemos na linguagem rápida"*: o código limpo onde pensa o problema **já é** o binário nativo de produção. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Dor n.º 2: "Ler código alheio (ou o seu de há 6 meses) é brincar aos detetives" |
| 27 | |
| 28 | Veja como escrevemos lógica condicional em quase todas as linguagens mainstream: |
| 29 | |
| 30 | ```text |
| 31 | if (estado == ATIVO && (papel == ADMIN || (papel == EDITOR && temPermissao))) { |
| 32 | if (quota > 0) { |
| 33 | return processar(utilizador); |
| 34 | } else { |
| 35 | return negar("Sem quota"); |
| 36 | } |
| 37 | } else { |
| 38 | return negar("Não autorizado"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | Os nossos olhos não evoluíram para emparelhar chavetas aninhadas nem para desemaranhar árvores de `if / else` em escada. Quando engenheiros, matemáticos ou físicos querem entender regras complexas sem se enganarem, não escrevem parágrafos de `if / else`: **desenham uma tabela**. |
| 43 | |
| 44 | Em Ridgun, **o código é essa tabela**: |
| 45 | |
| 46 | ```ridgun |
| 47 | autorizar String String Bool Int => String |
| 48 | autorizar "ativo" "admin" _ quota = |
| 49 | | quota > 0 = "A processar pedido" |
| 50 | | otherwise = "Negado: sem quota" |
| 51 | autorizar "ativo" "editor" True quota = |
| 52 | | quota > 0 = "A processar pedido" |
| 53 | | otherwise = "Negado: sem quota" |
| 54 | autorizar _ _ _ _ = "Negado: não autorizado" |
| 55 | ``` |
| 56 | |
| 57 | Repare nas colunas: |
| 58 | - Coluna 1: o estado (`"ativo"` ou `_`). |
| 59 | - Coluna 2: o papel (`"admin"`, `"editor"` ou `_`). |
| 60 | - Coluna 3: se tem permissão (`True` ou `_`). |
| 61 | - Coluna 4: a `quota`. |
| 62 | |
| 63 | Não precisa de executar mentalmente o programa linha por linha para saber o que acontece quando entra um `"editor"` com permissão `False`: os seus olhos descem verticalmente pela tabela e veem-no em meio segundo. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Dor n.º 3: "O meu computador tem 16 núcleos, mas o meu programa só usa 1 (porque tenho pânico de mexer em threads)" |
| 68 | |
| 69 | Hoje até o telemóvel mais modesto tem 8 núcleos, e qualquer servidor ou portátil de trabalho tem entre 12 e 32. No entanto, 95% dos programas do dia a dia continuam a ser executados numa única thread sequencial. |
| 70 | |
| 71 | Porquê? Porque nas linguagens imperativas, paralelizar significa abrir a caixa de Pandora: |
| 72 | - E se duas threads modificarem o mesmo dicionário ao mesmo tempo? (*Data race*). |
| 73 | - E se eu bloquear o `Mutex` A e depois o B numa thread, e o B e depois o A noutra? (*Deadlock*). |
| 74 | - E se eu contaminar toda a minha base de código com `async` e `await` de cima a baixo? |
| 75 | |
| 76 | Em Ridgun, **toda a função é pura e todo o dado é imutável por omissão**. Isso tem uma consequência mágica: **duas expressões que não dependem uma da outra jamais podem interferir entre si**. |
| 77 | |
| 78 | Por isso, em Ridgun não precisa de gerir threads para aproveitar o seu CPU. Só declara **o que depende do quê** com barras paralelas `||` e setas `->`: |
| 79 | |
| 80 | ```ridgun |
| 81 | relatorio mensal Dados => Relatorio |
| 82 | relatorio mensal d = combinar metricas_rede metricas_disco metricas_cpu |
| 83 | || analisar rede d -> metricas_rede |
| 84 | || analisar disco d -> metricas_disco |
| 85 | || analisar cpu d -> metricas_cpu |
| 86 | ``` |
| 87 | |
| 88 | Só escreveu três linhas limpas indicando de onde sai cada dado. O compilador vê que os três ramos são independentes e sabe que pode lançá-los em paralelo sem que exista a mais mínima possibilidade de um *data race* ou de um *deadlock*. |
| 89 | |
| 90 | E se precisar de agentes persistentes com caixa de correio e estado isolado (por exemplo, para manter milhares de sessões de clientes, trabalhadores em segundo plano ou ligações de rede)? Em Ridgun não precisa de definir classes `Actor` nem usar `Mutex`: **qualquer função normal converte-se num agente autónomo numa única linha com `agent`**: |
| 91 | |
| 92 | ```ridgun |
| 93 | contador = agent (somar visita) 0 |
| 94 | send 1 to contador |
| 95 | send 5 to contador |
| 96 | total = receive from contador |
| 97 | stop contador |
| 98 | ``` |
| 99 | |
| 100 | Além disso, o compilador realiza uma **escolha inteligente em tempo de compilação**: analisa estaticamente a profundidade e o custo de cada função em `||`, `||map`, `||filter` e `agent` para executar as funções leves como passos *green-thread* de sobrecarga zero e despachar automaticamente os cálculos pesados ou recursivos para threads reais do kernel em múltiplos núcleos (`SYS_clone`). |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Dor n.º 4: "O coletor de lixo decide pausar o mundo mesmo no pior momento" |
| 105 | |
| 106 | As linguagens funcionais clássicas (Haskell, OCaml, Erlang, Clojure) são maravilhosas para raciocinar sobre o código, mas quase todas dependem de um **Coletor de Lixo (*Garbage Collector*)** em tempo de execução. Isso descarta-as quando precisa de: |
| 107 | - Latências previsíveis ao microssegundo. |
| 108 | - Controlar exatamente quanta memória RAM uma operação pode consumir antes de a abortar de forma limpa. |
| 109 | - Escrever utilitários do sistema operativo (`ls`, `find`, `sh`, `doas`, `make`) ou tocar em registos de hardware (`MMIO`). |
| 110 | |
| 111 | O Ridgun dá-lhe **toda a elegância do mundo funcional puro, mas com Gestão de Memória do Ridgun (`RMM`: `RMM1` estática e `RMM2` por contagem de atribuições em cascata) mais regiões léxicas com quota exata (`with region`)**: |
| 112 | |
| 113 | ```ridgun |
| 114 | analisar pacote String => Result |
| 115 | analisar pacote bruto = |
| 116 | with region 16_KB do |
| 117 | descodificar trama bruto |
| 118 | ``` |
| 119 | |
| 120 | Cada estrutura temporária é libertada de forma determinística pelo compilador (`RMM1` / `RMM2`) no instante em que deixa de ser usada, e se um atacante lhe enviar um pacote malicioso desenhado para rebentar a memória, ao ultrapassar o byte 16.384 a região é cortada no instante, liberta a sua arena em $O(1)$ e devolve `Err OutOfMemory`. Sem quedas do processo, sem pausas de GC e sem fugas de memória. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## Em resumo: Quando é que o Ridgun brilha? |
| 125 | |
| 126 | O Ridgun foi feito para si se alguma vez quis: |
| 127 | |
| 128 | 1. **Escrever ferramentas de sistemas, compiladores, analisadores ou utilitários CLI** que arranquem em microssegundos, pesem apenas algumas centenas de KB e não possam quebrar por ponteiros nulos nem efeitos colaterais ocultos. |
| 129 | 2. **Construir sistemas concorrentes e servidores baseados no Modelo Nativo de Agentes (`agent`, `send ... to`, `receive from`, `stop`)**, onde qualquer função pura se converte num agente com ou sem estado isolado e o compilador escolhe inteligentemente entre execução *green-thread* ultraleve e threads nativas multinúcleo. |
| 130 | 3. **Programar algoritmos, pipelines de dados ou álgebra de tensores** onde o compilador verifique por si até as dimensões das matrizes (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) antes de executar o que quer que seja. |
| 131 | 4. **Desfrutar a ler o seu próprio código**: que cada ficheiro `.rg` pareça uma página de um livro bem paginado graças ao formatador tabular automático (`stable format`). |
| 132 | |
| 133 | Ficou com curiosidade? Passe ao **Capítulo 01** e vamos escrever o nosso primeiro programa em menos de dois minutos. |