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-do-ridgun.md133 lines · 8.2 KBRaw
1# Capítulo 00: Por que preciso do Ridgun? Ah, é? Não sabia... Conte-me mais!
2
3Sejamos 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
9Vamos dar-lhes um nome.
10
11---
12
13## Dor n.º 1: "A síndrome dos dois mundos" (Prototipar num, reescrever noutro)
14
15De certeza que já viveu este filme:
16
171. 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.
182. 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.
193. 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?**
22Em 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
28Veja como escrevemos lógica condicional em quase todas as linguagens mainstream:
29
30```text
31if (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
42Os 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
44Em Ridgun, **o código é essa tabela**:
45
46```ridgun
47autorizar String String Bool Int => String
48autorizar "ativo" "admin" _ quota =
49 | quota > 0 = "A processar pedido"
50 | otherwise = "Negado: sem quota"
51autorizar "ativo" "editor" True quota =
52 | quota > 0 = "A processar pedido"
53 | otherwise = "Negado: sem quota"
54autorizar _ _ _ _ = "Negado: não autorizado"
55```
56
57Repare 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
63Nã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
69Hoje 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
71Porquê? 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
76Em 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
78Por 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
81relatorio mensal Dados => Relatorio
82relatorio 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
88Só 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
90E 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
93contador = agent (somar visita) 0
94send 1 to contador
95send 5 to contador
96total = receive from contador
97stop contador
98```
99
100Alé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
106As 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
111O 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
114analisar pacote String => Result
115analisar pacote bruto =
116 with region 16_KB do
117 descodificar trama bruto
118```
119
120Cada 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
126O Ridgun foi feito para si se alguma vez quis:
127
1281. **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.
1292. **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.
1303. **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.
1314. **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
133Ficou com curiosidade? Passe ao **Capítulo 01** e vamos escrever o nosso primeiro programa em menos de dois minutos.