Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capitolo 00: Perché ho bisogno di Ridgun? Ah sì? Non lo sapevo... Raccontami di più! |
| 2 | |
| 3 | Siamo sinceri: quando qualcuno ti dice *"abbiamo creato un nuovo linguaggio di programmazione"*, la tua prima reazione naturale — e sanissima — è inarcare un sopracciglio e pensare: |
| 4 | |
| 5 | > *"Un altro? Ma se ho già Python per andare veloce, Rust o C per quando mi serve velocità pura, e Bash per incollare cose nel terminale. Perché diavolo dovrei imparare Ridgun?"* |
| 6 | |
| 7 | È una domanda eccellente. E la risposta breve è: **perché ti sei abituato a soffrire quattro dolori quotidiani pensando che fossero inevitabili**. |
| 8 | |
| 9 | Diamo loro un nome. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Dolore n.º 1: "La sindrome dei due mondi" (Prototipare in uno, riscrivere nell'altro) |
| 14 | |
| 15 | Sicuramente hai già vissuto questo film: |
| 16 | |
| 17 | 1. Inizi uno strumento o un motore di calcolo in **Python**, **JavaScript** o **Ruby** perché vuoi pensare al problema, non ai puntatori né al compilatore. |
| 18 | 2. Il codice viene corto e bello... ma quando gli passi un file da 500 MB o provi a usare i 16 core della tua macchina, si trascina. E se vuoi distribuire il tuo programma su un server pulito, devi impacchettare mezzo gigabyte di ambiente virtuale, interprete e librerie dinamiche. |
| 19 | 3. Rassegnato, decidi di riscriverlo in **C**, **C++** o **Rust**. Ora vola e occupa poche centinaia di kilobyte, ma il tuo codice di 40 righe si è trasformato in 300 righe piene di `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, parentesi graffe `{}` e punti e virgola `;`. |
| 20 | |
| 21 | **Cosa cambia con Ridgun?** |
| 22 | In Ridgun scrivi codice **più corto e pulito che in Python**, ma quando esegui `ridgunc build`, ottieni **un unico binario nativo ELF da ~350 KB** senza interprete, senza macchina virtuale esterna e senza garbage collector. Non c'è la fase *"poi lo riscriviamo nel linguaggio veloce"*: il codice pulito in cui ragioni sul problema **è già** il binario nativo di produzione. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Dolore n.º 2: "Leggere codice altrui (o il tuo di 6 mesi fa) è giocare al detective" |
| 27 | |
| 28 | Guarda come scriviamo la logica condizionale in quasi tutti i linguaggi mainstream: |
| 29 | |
| 30 | ```text |
| 31 | if (stato == ATTIVO && (ruolo == ADMIN || (ruolo == EDITOR && haPermesso))) { |
| 32 | if (quota > 0) { |
| 33 | return elabora(utente); |
| 34 | } else { |
| 35 | return nega("Senza quota"); |
| 36 | } |
| 37 | } else { |
| 38 | return nega("Non autorizzato"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | I nostri occhi non si sono evoluti per accoppiare parentesi graffe annidate né per sbrogliare alberi di `if / else` a gradini. Quando gli ingegneri, i matematici o i fisici vogliono capire regole complesse senza sbagliare, non scrivono paragrafi di `if / else`: **disegnano una tabella**. |
| 43 | |
| 44 | In Ridgun, **il codice è quella tabella**: |
| 45 | |
| 46 | ```ridgun |
| 47 | autorizzare String String Bool Int => String |
| 48 | autorizzare "attivo" "admin" _ quota = |
| 49 | | quota > 0 = "Elaborazione richiesta" |
| 50 | | otherwise = "Negato: senza quota" |
| 51 | autorizzare "attivo" "editor" True quota = |
| 52 | | quota > 0 = "Elaborazione richiesta" |
| 53 | | otherwise = "Negato: senza quota" |
| 54 | autorizzare _ _ _ _ = "Negato: non autorizzato" |
| 55 | ``` |
| 56 | |
| 57 | Guarda le colonne: |
| 58 | - Colonna 1: lo stato (`"attivo"` o `_`). |
| 59 | - Colonna 2: il ruolo (`"admin"`, `"editor"` o `_`). |
| 60 | - Colonna 3: se ha il permesso (`True` o `_`). |
| 61 | - Colonna 4: la `quota`. |
| 62 | |
| 63 | Non devi eseguire mentalmente il programma riga per riga per sapere cosa succede quando entra un `"editor"` con permesso `False`: i tuoi occhi scendono verticalmente lungo la tabella e lo vedono in mezzo secondo. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Dolore n.º 3: "Il mio computer ha 16 core, ma il mio programma ne usa solo 1 (perché ho il terrore di toccare i thread)" |
| 68 | |
| 69 | Oggi persino il telefono più modesto ha 8 core, e qualsiasi server o portatile di lavoro ne ha tra 12 e 32. Eppure, il 95% dei programmi quotidiani continua a girare su un singolo thread sequenziale. |
| 70 | |
| 71 | Perché? Perché nei linguaggi imperativi parallelizzare significa aprire il vaso di Pandora: |
| 72 | - E se due thread modificano lo stesso dizionario contemporaneamente? (*Data race*). |
| 73 | - E se blocco il `Mutex` A e poi il B in un thread, e il B e poi l'A in un altro? (*Deadlock*). |
| 74 | - E se contamino tutta la mia base di codice con `async` e `await` da cima a fondo? |
| 75 | |
| 76 | In Ridgun, **ogni funzione è pura e ogni dato è immutabile per impostazione predefinita**. Questo ha una conseguenza magica: **due espressioni che non dipendono l'una dall'altra non possono mai interferire tra loro**. |
| 77 | |
| 78 | Perciò, in Ridgun non devi gestire thread per sfruttare la tua CPU. Dichiari solo **cosa dipende da cosa** con barre parallele `||` e frecce `->`: |
| 79 | |
| 80 | ```ridgun |
| 81 | rapporto mensile Dati => Rapporto |
| 82 | rapporto mensile d = combina metriche_rete metriche_disco metriche_cpu |
| 83 | || analizza rete d -> metriche_rete |
| 84 | || analizza disco d -> metriche_disco |
| 85 | || analizza cpu d -> metriche_cpu |
| 86 | ``` |
| 87 | |
| 88 | Tu hai scritto solo tre righe pulite indicando da dove esce ogni dato. Il compilatore vede che i tre rami sono indipendenti e sa che può lanciarli in parallelo senza che esista nemmeno la minima possibilità di un *data race* o di un *deadlock*. |
| 89 | |
| 90 | E se hai bisogno di agenti persistenti con casella di posta e stato isolato (ad esempio per mantenere migliaia di sessioni client, worker in background o connessioni di rete)? In Ridgun non devi definire classi `Actor` né usare `Mutex`: **qualsiasi funzione normale si trasforma in un agente autonomo in una sola riga con `agent`**: |
| 91 | |
| 92 | ```ridgun |
| 93 | contatore = agent (somma visita) 0 |
| 94 | send 1 to contatore |
| 95 | send 5 to contatore |
| 96 | totale = receive from contatore |
| 97 | stop contatore |
| 98 | ``` |
| 99 | |
| 100 | Inoltre, il compilatore effettua una **scelta intelligente a tempo di compilazione**: analizza staticamente la profondità e il costo di ogni funzione in `||`, `||map`, `||filter` e `agent` per eseguire le funzioni leggere come passi *green-thread* a sovraccarico zero (senza pagare il costo di riscaldare thread del sistema operativo) e smistare automaticamente i calcoli pesanti o ricorsivi su thread reali del kernel su più core (`SYS_clone`). |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Dolore n.º 4: "Il garbage collector decide di mettere in pausa il mondo proprio nel momento peggiore" |
| 105 | |
| 106 | I linguaggi funzionali classici (Haskell, OCaml, Erlang, Clojure) sono meravigliosi per ragionare sul codice, ma quasi tutti dipendono da un **Garbage Collector** a tempo di esecuzione. Questo li esclude quando hai bisogno di: |
| 107 | - Latenze prevedibili al microsecondo. |
| 108 | - Controllare esattamente quanta memoria RAM può consumare un'operazione prima di interromperla in modo pulito. |
| 109 | - Scrivere utilità di sistema operativo (`ls`, `find`, `sh`, `doas`, `make`) o toccare registri hardware (`MMIO`). |
| 110 | |
| 111 | Ridgun ti dà **tutta l'eleganza del mondo funzionale puro, ma con la Gestione della Memoria di Ridgun (`RMM`: `RMM1` statica e `RMM2` per conteggio di assegnazioni a cascata) più regioni lessicali con quota esatta (`with region`)**: |
| 112 | |
| 113 | ```ridgun |
| 114 | analizza pacchetto String => Result |
| 115 | analizza pacchetto grezzo = |
| 116 | with region 16_KB do |
| 117 | decodifica trama grezzo |
| 118 | ``` |
| 119 | |
| 120 | Ogni struttura temporanea viene liberata in modo deterministico dal compilatore (`RMM1` / `RMM2`) nell'istante in cui smette di essere usata, e se un attaccante ti invia un pacchetto malevolo progettato per far esplodere la memoria, superato il byte 16.384 la regione viene tagliata all'istante, libera la sua arena in $O(1)$ e ti restituisce `Err OutOfMemory`. Senza crolli del processo, senza pause del GC e senza perdite di memoria. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## In sintesi: Quando brilla Ridgun? |
| 125 | |
| 126 | Ridgun è fatto per te se hai mai voluto: |
| 127 | |
| 128 | 1. **Scrivere strumenti di sistema, compilatori, analizzatori o utilità CLI** che si avviino in microsecondi, pesino poche centinaia di KB e non possano rompersi per puntatori nulli né effetti collaterali nascosti. |
| 129 | 2. **Costruire sistemi concorrenti e server basati sul Modello Nativo di Agenti (`agent`, `send ... to`, `receive from`, `stop`)**, dove qualsiasi funzione pura diventa un agente con o senza stato isolato e il compilatore sceglie in modo intelligente tra esecuzione *green-thread* ultraleggera e thread nativi multicore. |
| 130 | 3. **Programmare algoritmi, pipeline di dati o algebra dei tensori** in cui il compilatore verifichi per te persino le dimensioni delle matrici (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) prima di eseguire alcunché. |
| 131 | 4. **Divertirti leggendo il tuo stesso codice**: che ogni file `.rg` sembri la pagina di un libro ben impaginato grazie al formattatore tabellare automatico (`stable format`). |
| 132 | |
| 133 | Ti pizzica la curiosità? Passa al **Capitolo 01** e scriviamo il nostro primo programma in meno di due minuti. |