Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Capitolo 03: Funzioni, firme dei tipi e l'arte di scrivere codice come una tabella |
| 2 | |
| 3 | C'è una domanda che ogni programmatore si fa la prima volta che vede codice funzionale: |
| 4 | |
| 5 | > *"Se qui non ci sono classi, né oggetti, né variabili che cambiano valore... allora dove metto la mia logica?"* |
| 6 | |
| 7 | Nelle **funzioni**. Ma non in quelle funzioni da 200 righe piene di flag booleani che tutti abbiamo sofferto qualche volta, bensì in funzioni piccole, dirette e così facili da leggere che sembrano una tabella su una lavagna. |
| 8 | |
| 9 | Vediamo come si costruiscono. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 3.1. Due modi di presentare una funzione (Scegli tu l'abito) |
| 14 | |
| 15 | In Ridgun ogni funzione inizia con la sua **firma dei tipi** (cosa entra e cosa esce dopo `=>`) e sotto vanno le sue **clausole di implementazione** (con `=`). |
| 16 | |
| 17 | Puoi scrivere l'intestazione in due modi: |
| 18 | |
| 19 | ### Opzione A: Dritta al punto (senza prefisso) |
| 20 | ```ridgun |
| 21 | applica sconto Int Int => Int |
| 22 | applica sconto prezzo percentuale = prezzo - (prezzo * percentuale / 100) |
| 23 | ``` |
| 24 | |
| 25 | ### Opzione B: Con etichetta esplicita (`fn ::`) |
| 26 | ```ridgun |
| 27 | fn :: applica sconto Int Int => Int |
| 28 | applica sconto prezzo percentuale = prezzo - (prezzo * percentuale / 100) |
| 29 | ``` |
| 30 | |
| 31 | *"Ehi, perché usiamo spazi in `applica sconto` invece di `applica_sconto`?"* |
| 32 | Perché in Ridgun tutti i tipi iniziano con la maiuscola (`Int`, `String`, `A`..`Z`), `[` o `(`, mentre le parole del nome della funzione vanno in minuscolo. Così puoi scrivere nomi di più parole con spazi naturali (`applica sconto`, `costo spedizione`, `str len`, `read file`) senza alcuna ambiguità per il compilatore. |
| 33 | |
| 34 | *"E perché esistono entrambe le opzioni (con `fn ::` e senza)?"* |
| 35 | Perché i gusti sono gusti: |
| 36 | - C'è chi preferisce che le funzioni inizino attaccate al margine sinistro senza scrivere `fn ::` ogni volta (in fin dei conti, la freccia `=>` grida già ai quattro venti che quella riga è una firma dei tipi). |
| 37 | - E c'è chi preferisce avere una colonna sinistra dedicata alle etichette (`fn ::`, `op ::`, `type`, `import`) per scansionare visivamente il file. |
| 38 | |
| 39 | Il compilatore e `stable format` capiscono entrambe alla perfezione. |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## 3.2. La magia delle colonne: perché i tuoi occhi riposano |
| 44 | |
| 45 | Guarda questa funzione che calcola il costo di una spedizione in base al paese e all'urgenza: |
| 46 | |
| 47 | ```ridgun |
| 48 | fn :: costo spedizione String Bool Int => Int |
| 49 | costo spedizione "IT" False _ = 5 |
| 50 | costo spedizione "IT" True _ = 12 |
| 51 | costo spedizione _ False peso = 10 + (peso * 2) |
| 52 | costo spedizione _ True peso = 25 + (peso * 4) |
| 53 | ``` |
| 54 | |
| 55 | Fai la prova: copri la metà destra dello schermo e guarda solo le tre colonne di ingresso: |
| 56 | - Prima colonna (`String`): `"IT"`, `"IT"`, `_`, `_`. |
| 57 | - Seconda colonna (`Bool`): `False`, `True`, `False`, `True`. |
| 58 | - Terza colonna (`Int`): `_`, `_`, `peso`, `peso`. |
| 59 | |
| 60 | Vedi qualche buco logico? Nessuno: ci sono tutte le 4 combinazioni possibili davanti ai tuoi occhi, allineate verticalmente sotto `String`, `Bool` e `Int`. In un blocco `if / else` tradizionale dovresti leggere 18 righe per essere sicuro di non aver lasciato fuori nessun caso. |
| 61 | |
| 62 | --- |
| 63 | |
| 64 | ## 3.3. I tipi di base e un regalo per non contare gli zeri (`_KB`, `_MB`, `_GB`) |
| 65 | |
| 66 | I tipi scalari di Ridgun sono diretti e senza sorprese: |
| 67 | |
| 68 | | Tipo | Cos'è in realtà | Esempi | |
| 69 | |---|---|---| |
| 70 | | `Int` | Intero a 64 bit con segno (`i64`) | `0`, `42`, `-100`, `0xFF00`, `1_000_000` | |
| 71 | | `U64` | Intero a 64 bit senza segno (`u64`) per crittografia e sistemi | `0xdeadbeefcafe0001`, `a xor b`, `x rotr 24` | |
| 72 | | `Byte` | Ottetto senza segno (`0..255`) in tensori e buffer binari | `Tensor[Byte * 64]` | |
| 73 | | `Real` / `Float` | Decimale a doppia precisione (`f64`) | `3.14159`, `0.001`, `-2.5` | |
| 74 | | `String` | Stringa di testo o buffer binario immutabile con lunghezza esplicita | `"Ciao"`, `'percorso\grezzo'`, `sha256 raw "abc"` | |
| 75 | | `Char` | Codice carattere | Restituito da `str char at` | |
| 76 | | `Bool` | Booleano classico | `True`, `False` | |
| 77 | | `()` | Vuoto (*Unit*, quando stampi soltanto o fai I/O) | `()` | |
| 78 | |
| 79 | ### Operatori a livello di bit da 1 ciclo e flessibilità lessicale (`fn`, `op`, `where`) |
| 80 | Per la crittografia, i protocolli binari e i sistemi ad alte prestazioni, `Int` e `U64` dispongono di operatori nativi a livello di bit e rotazione compilati in una singola istruzione macchina: |
| 81 | - **Logici e bit a bit**: `and`, `or`, `xor`, `not` |
| 82 | - **Scorrimenti e rotazioni a 64 bit**: `shl` (sinistra), `shr` (destra logica senza segno), `rotl` (rotazione a sinistra), `rotr` (rotazione a destra) |
| 83 | |
| 84 | Inoltre, il vocabolario di Ridgun è deliberatamente minimo e ortogonale: |
| 85 | - **`where` non è una parola riservata**: i grafi paralleli si esprimono esclusivamente con `||`, perciò `where` è un normale identificatore che puoi usare come nome di variabile o funzione. |
| 86 | - **`fn` e `op` possono far parte dei nomi**: dopo `fn ::` o `op ::`, le stesse parole `fn` e `op` possono comparire come parte del nome della tua funzione o del tuo operatore (`fn :: op Int => Int`, `op :: Int fn Int => Int`, `op :: A op B => A`). |
| 87 | - **Ritorni multipli separati da spazi (`=> Int Int`)**: quando una funzione restituisce più valori, la sua uscita si scrive separando i tipi con spazi (`fn :: dividi Int Int => Int Int`), e nella chiamata puoi destrutturarla direttamente senza allocare tuple nell'heap (`quoziente, resto = dividi 17 5`). |
| 88 | |
| 89 | ### Stanco di scrivere `65536` o `1048576` e pregare di non aver saltato una cifra? |
| 90 | In Ridgun i letterali numerici accettano direttamente i suffissi di memoria **`_KB`**, **`_MB`** e **`_GB`** e i suffissi di tempo **`_ms`**, **`_s`** e **`_min`**, oltre ai separatori numerici: |
| 91 | |
| 92 | ```ridgun |
| 93 | fn :: dimensione buffer => Int |
| 94 | dimensione buffer = 64_KB -- Il compilatore lo traduce in 65536 |
| 95 | |
| 96 | fn :: limite file => Int |
| 97 | limite file = 16_MB -- Il compilatore lo traduce in 16777216 |
| 98 | |
| 99 | fn :: maschera ottetto => Int |
| 100 | maschera ottetto = 0xFF_00 -- Esadecimale pulito e leggibile |
| 101 | ``` |
| 102 | |
| 103 | --- |
| 104 | |
| 105 | ## 3.4. Virgolette doppie (`"..."`) vs. Virgolette singole (`'...'`) |
| 106 | |
| 107 | In molti linguaggi le virgolette singole e doppie fanno esattamente la stessa cosa (uno spreco). In Ridgun ciascuna ha una missione chiarissima: |
| 108 | |
| 109 | ### 1. Virgolette doppie (`"..."`): Intelligenti e interpolate |
| 110 | Interpretano gli escape (`\n`, `\t`, `\"`, `\\`) e **valutano qualsiasi variabile o espressione che metti tra parentesi graffe `{...}`**: |
| 111 | |
| 112 | ```ridgun |
| 113 | fn :: riepilogo ordine String Int Int => String |
| 114 | riepilogo ordine voce unita prezzo = |
| 115 | "Hai ordinato {unita}x {voce}. Totale da pagare: {unita * prezzo} euro." |
| 116 | ``` |
| 117 | |
| 118 | *(Trucco: se devi scrivere una parentesi graffa letterale dentro le virgolette doppie — ad esempio generando uno script di shell `"$\{VAR:-default\}"` o un JSON —, puoi farne l'escape con la barra rovesciata `\{` e `\}` o raddoppiarla `{{` e `}}`).* |
| 119 | |
| 120 | ### 2. Virgolette singole (`'...'`): Grezze e letterali (*Raw Strings*) |
| 121 | Quello che scrivi tra virgolette singole esce **così com'è**, senza toccare nemmeno una barra rovesciata `\`, né una parentesi graffa `{}`. Perfetto per quando generi codice in un altro linguaggio, scrivi comandi di shell o lavori con percorsi pieni di barre: |
| 122 | |
| 123 | ```ridgun |
| 124 | fn :: comando sed => String |
| 125 | comando sed = 's/\([0-9]+\)/[\1]/g' |
| 126 | ``` |
| 127 | |
| 128 | ### La tua cassetta degli attrezzi per le stringhe (`String`) |
| 129 | Unire stringhe è semplice come sommarle con `+`: `"Ciao, " + nome`. E quando hai bisogno di fare chirurgia di precisione su un testo, hai queste funzioni native con spazi naturali pronte senza importare nulla: |
| 130 | |
| 131 | - `str len s` $\rightarrow$ lunghezza di `s`. |
| 132 | - `str char at s i` $\rightarrow$ codice numerico (`Int`) del carattere nella posizione `i` (`-1` se esci dai limiti). |
| 133 | - `str slice s inizio fine` $\rightarrow$ ritaglia da `inizio` fino a `fine` (senza includere `fine`). |
| 134 | - `char from code c` $\rightarrow$ converte un codice numerico (`Int`, es. `10` per `\n` o `34` per `"`) in uno `String` di 1 carattere. |
| 135 | - `str contains s ago` e `str starts with s prefisso` $\rightarrow$ restituiscono `True` o `False`. |
| 136 | - `int to str n` e `str to int s` $\rightarrow$ convertono da numero a testo e viceversa (`str to int` capisce persino `"0xFF"`!). |
| 137 | |
| 138 | --- |
| 139 | |
| 140 | ## 3.5. Blocchi su più righe, la regola universale dell'`=` e variabili `mut` |
| 141 | |
| 142 | Quando una funzione non entra in una sola riga, vai a capo dopo l'`=` e indenti il blocco. |
| 143 | |
| 144 | Guarda come si legge un blocco passo dopo passo: |
| 145 | |
| 146 | ```ridgun |
| 147 | fn :: calcola fattura Int Int => Int |
| 148 | calcola fattura base iva = |
| 149 | imposte = base * iva / 100 |
| 150 | gestione = 5 |
| 151 | echo "Dettaglio -> Base:" base "Imposte:" imposte |
| 152 | base + imposte + gestione |
| 153 | ``` |
| 154 | |
| 155 | Quattro regole d'oro che rendono i blocchi in Ridgun puliti e uniformi: |
| 156 | 1. **Immutabile per impostazione predefinita senza `let` né `const`**: scrivi `imposte = ...` e hai già la tua costante locale immutabile. Se per distrazione provi a sovrascrivere `imposte = 99` più sotto nello stesso blocco, il compilatore ti ferma su due piedi. |
| 157 | 2. **Variabili mutabili esplicite con `mut`**: quando vuoi davvero aggiornare un accumulatore locale passo dopo passo dentro una funzione, lo dichiari con **`mut`** e poi lo riassegni liberamente con `=`: |
| 158 | ```ridgun |
| 159 | fn :: applica supplementi Int => Int |
| 160 | applica supplementi prezzo = |
| 161 | mut totale = prezzo |
| 162 | totale = totale + 10 |
| 163 | totale = totale * 2 |
| 164 | totale |
| 165 | ``` |
| 166 | *(E se `totale` conserva una lista o una struttura in memoria, la politica **`RMM2`** di Ridgun somma `+1` al nuovo valore prima di scalare `-1` dal vecchio, liberandolo all'istante se nessun altro lo usa).* |
| 167 | 3. **La regola universale dell'`=`**: dietro **qualsiasi `=`** (in una funzione, in un operatore, in una variabile locale `x =` o in un ramo condizionale) puoi mettere sempre una qualsiasi delle tre forme di blocco di Ridgun: |
| 168 | - **`(nulla)`**: righe sequenziali indentate (come sopra). |
| 169 | - **`|`**: un albero di guardie condizionali. |
| 170 | - **`||`**: un grafo di calcoli in parallelo con `->`. |
| 171 | 4. **L'ultima espressione è sempre il risultato**: `base + imposte + gestione` è ciò che restituisce `calcola fattura`. Senza `return`. |
| 172 | |
| 173 | Nel **Capitolo 04** vedremo come prendere decisioni complesse — inclusi alberi decisionali su più livelli con `|.` — senza scrivere mai un `if/else` né un ciclo `while`: benvenuto nel mondo delle **guardie booleane** e della **ricorsione di coda a costo zero**. |