Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Kapitel 00: Varför behöver jag Ridgun? Jaså? Det visste jag inte... Berätta mer! |
| 2 | |
| 3 | Låt oss vara ärliga: när någon säger *"vi har skapat ett nytt programmeringsspråk"* är din första naturliga — och mycket sunda — reaktion att höja på ögonbrynet och tänka: |
| 4 | |
| 5 | > *"Ännu ett? Men jag har ju redan Python när det ska gå fort, Rust eller C när jag behöver prestanda, och Bash för att klistra ihop saker i terminalen. Varför i hela friden skulle jag behöva lära mig Ridgun?"* |
| 6 | |
| 7 | Det är en utmärkt fråga. Och det korta svaret är: **därför att du har vant dig vid att lida av tre dagliga plågor i tron att de var oundvikliga**. |
| 8 | |
| 9 | Låt oss sätta namn på dem. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## Plåga nr 1: "Tvåvärldssyndromet" (Prototypa i ett språk, skriva om i ett annat) |
| 14 | |
| 15 | Du har säkert varit med om den här filmen: |
| 16 | |
| 17 | 1. Du påbörjar ett verktyg eller en beräkningsmotor i **Python**, **JavaScript** eller **Ruby** eftersom du vill tänka på problemet, inte på pekare eller kompilatorn. |
| 18 | 2. Koden blir kort och vacker... men när du matar den med en fil på 500 MB eller försöker utnyttja alla 16 kärnor i din dator kryper den fram. Och om du vill distribuera ditt program till en ren server måste du paketera en halv gigabyte med virtuell miljö, tolk och dynamiska bibliotek. |
| 19 | 3. Uppgiven bestämmer du dig för att skriva om det i **C**, **C++** eller **Rust**. Nu flyger det fram och tar bara några hundra kilobyte, men din kod på 40 rader har förvandlats till 300 rader fulla av `unwrap()`, `&mut`, `'a`, `Arc<Mutex<...>>`, måsvingar `{}` och semikolon `;`. |
| 20 | |
| 21 | **Vad förändras med Ridgun?** |
| 22 | I Ridgun skriver du kod som är **kortare och renare än i Python**, men när du kör `ridgunc build` får du **en enda inbyggd ELF-binär på ~350 KB** utan tolk, utan extern virtuell maskin och utan skräpsamlare (*Garbage Collector*). Det finns ingen fas där man säger *"vi skriver om det i det snabba språket senare"*: den rena koden där du formulerar problemet **är redan** produktionsbinären. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Plåga nr 2: "Att läsa andras kod (eller din egen från för 6 månader sedan) är som att leka detektiv" |
| 27 | |
| 28 | Titta på hur vi skriver villkorslogik i nästan alla vanliga språk: |
| 29 | |
| 30 | ```text |
| 31 | if (status == AKTIV && (roll == ADMIN || (roll == REDAKTOR && harBehorighet))) { |
| 32 | if (kvot > 0) { |
| 33 | return bearbeta(anvandare); |
| 34 | } else { |
| 35 | return neka("Ingen kvot"); |
| 36 | } |
| 37 | } else { |
| 38 | return neka("Obehörig"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | Våra ögon har inte utvecklats för att para ihop nästlade måsvingar eller nysta upp trappstegsformade `if / else`-träd. När ingenjörer, matematiker eller fysiker vill förstå komplexa regler utan att göra fel skriver de inte stycken av `if / else`: **de ritar en tabell**. |
| 43 | |
| 44 | I Ridgun **är koden den tabellen**: |
| 45 | |
| 46 | ```ridgun |
| 47 | auktorisera String String Bool Int => String |
| 48 | auktorisera "aktiv" "admin" _ kvot = |
| 49 | | kvot > 0 = "Bearbetar begäran" |
| 50 | | otherwise = "Nekad: ingen kvot" |
| 51 | auktorisera "aktiv" "redaktor" True kvot = |
| 52 | | kvot > 0 = "Bearbetar begäran" |
| 53 | | otherwise = "Nekad: ingen kvot" |
| 54 | auktorisera _ _ _ _ = "Nekad: obehörig" |
| 55 | ``` |
| 56 | |
| 57 | Titta på kolumnerna: |
| 58 | - Kolumn 1: statusen (`"aktiv"` eller `_`). |
| 59 | - Kolumn 2: rollen (`"admin"`, `"redaktor"` eller `_`). |
| 60 | - Kolumn 3: om behörighet finns (`True` eller `_`). |
| 61 | - Kolumn 4: `kvot`. |
| 62 | |
| 63 | Du behöver inte köra programmet mentalt rad för rad för att veta vad som händer när en `"redaktor"` med behörigheten `False` kommer in: blicken glider vertikalt nedför tabellen och du ser det på en halv sekund. |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Plåga nr 3: "Min dator har 16 kärnor, men mitt program använder bara 1 (för att jag får panik av att röra trådar)" |
| 68 | |
| 69 | I dag har till och med den enklaste telefonen 8 kärnor, och varje server eller arbetsdator har mellan 12 och 32. Ändå körs 95 % av alla vardagliga program fortfarande i en enda sekventiell tråd. |
| 70 | |
| 71 | Varför? Därför att i imperativa språk innebär parallellisering att öppna Pandoras ask: |
| 72 | - Tänk om två trådar ändrar samma uppslagsverk samtidigt? (*Data race*). |
| 73 | - Tänk om jag låser `Mutex` A och sedan B i en tråd, och B och sedan A i en annan? (*Deadlock*). |
| 74 | - Tänk om jag förorenar hela min kodbas med `async` och `await` uppifrån och ner? |
| 75 | |
| 76 | I Ridgun **är varje funktion ren och all data oföränderlig (*immutable*) som standard**. Det får en magisk konsekvens: **två uttryck som inte är beroende av varandra kan aldrig någonsin störa varandra**. |
| 77 | |
| 78 | Därför behöver du inte hantera trådar i Ridgun för att utnyttja din processor. Du deklarerar bara **vad som beror på vad** med parallella streck `||` och pilar `->`: |
| 79 | |
| 80 | ```ridgun |
| 81 | manadsrapport Data => Rapport |
| 82 | manadsrapport d = kombinera nat_matt disk_matt cpu_matt |
| 83 | || analysera nat d -> nat_matt |
| 84 | || analysera disk d -> disk_matt |
| 85 | || analysera cpu d -> cpu_matt |
| 86 | ``` |
| 87 | |
| 88 | Du har bara skrivit tre rena rader som anger varifrån varje datamängd kommer. Kompilatorn ser att de tre grenarna är oberoende och vet att den kan starta dem parallellt utan minsta risk för vare sig *data race* eller *deadlock*. |
| 89 | |
| 90 | Och vad gör du om du behöver långlivade agenter med inkorg och isolerat tillstånd (till exempel för att hålla tusentals klientsessioner, bakgrundsarbetare eller nätverksanslutningar)? I Ridgun behöver du varken definiera `Actor`-klasser eller använda `Mutex`: **vilken vanlig funktion som helst blir en autonom agent på en enda rad med `agent`**: |
| 91 | |
| 92 | ```ridgun |
| 93 | raknare = agent (addera besok) 0 |
| 94 | send 1 to raknare |
| 95 | send 5 to raknare |
| 96 | totalt = receive from raknare |
| 97 | stop raknare |
| 98 | ``` |
| 99 | |
| 100 | Dessutom gör kompilatorn ett **smart val vid kompilering**: den analyserar statiskt djupet och kostnaden för varje funktion i `||`, `||map`, `||filter` och `agent` för att köra lätta funktioner som *green-thread*-steg utan overhead och automatiskt skicka tunga eller djupt rekursiva beräkningar till riktiga kärntrådar över flera CPU-kärnor (`SYS_clone`). |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Plåga nr 4: "Skräpsamlaren bestämmer sig för att pausa hela världen i exakt fel ögonblick" |
| 105 | |
| 106 | Klassiska funktionella språk (Haskell, OCaml, Erlang, Clojure) är underbara för att resonera kring kod, men nästan alla är beroende av en **skräpsamlare (*Garbage Collector*)** vid körning. Det utesluter dem när du behöver: |
| 107 | - Förutsägbara svarstider på mikrosekundnivå. |
| 108 | - Exakt kontroll över hur mycket RAM-minne en operation får förbruka innan den avbryts snyggt. |
| 109 | - Skriva operativsystemverktyg (`ls`, `find`, `sh`, `doas`, `make`) eller arbeta direkt mot hårdvaruregister (`MMIO`). |
| 110 | |
| 111 | Ridgun ger dig **hela elegansen från den rent funktionella världen, men med Ridguns minneshantering (`RMM`: statisk `RMM1` och kaskadräknad `RMM2`) plus lexikala regioner med exakt kvot (`with region`)**: |
| 112 | |
| 113 | ```ridgun |
| 114 | tolka paket String => Result |
| 115 | tolka paket radata = |
| 116 | with region 16_KB do |
| 117 | avkoda ram radata |
| 118 | ``` |
| 119 | |
| 120 | Varje tillfällig struktur frigörs deterministiskt av kompilatorn (`RMM1` / `RMM2`) i samma ögonblick som den slutar användas, och om en angripare skickar ett skadligt paket utformat för att spränga minnet klipps regionen omedelbart när den passerar byte 16 384, frigör sin arena i $O(1)$ och returnerar `Err OutOfMemory`. Inga kraschade processer, inga GC-pauser och inga minnesläckor. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## Sammanfattningsvis: När glänser Ridgun? |
| 125 | |
| 126 | Ridgun är skapat för dig om du någonsin har velat: |
| 127 | |
| 128 | 1. **Skriva systemverktyg, kompilatorer, parsrar eller CLI-verktyg** som startar på mikrosekunder, väger några hundra KB och inte kan krascha på grund av null-pekare eller dolda sidoeffekter. |
| 129 | 2. **Bygga samtidiga system och servrar med den inbyggda agentmodellen (`agent`, `send ... to`, `receive from`, `stop`)**, där varje ren funktion kan bli en agent med eller utan isolerat tillstånd och kompilatorn smart väljer mellan ultralätta *green-thread*-steg och flerkärniga operativsystemtrådar. |
| 130 | 3. **Programmera algoritmer, datapipelines eller tensoralgebra** där kompilatorn kontrollerar ända ner till matrisdimensionerna åt dig (`Tensor[M, K] * Tensor[K, N] => Tensor[M, N]`) innan något ens körs. |
| 131 | 4. **Njuta av att läsa din egen kod**: låta varje `.rg`-fil se ut som en sida ur en välformgiven bok tack vare den automatiska tabellformateraren (`stable format`). |
| 132 | |
| 133 | Har vi väckt din nyfikenhet? Gå vidare till **Kapitel 01** så skriver vi vårt första program på under två minuter. |