Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Chapter 02: Your Two Best Friends: `ridgunc` and `stable` (Zero YAML Files, Promised) |
| 2 | |
| 3 | Some languages have a wonderful compiler, but a project tooling ecosystem that feels like it was designed by your worst enemy: a 400-line `Makefile` with invisible tabs, an endless `pom.xml`, or a `webpack.config.js` that stops working if you look at it funny... |
| 4 | |
| 5 | In Ridgun we made ourselves a very simple promise on day one: |
| 6 | |
| 7 | > *"There will be only two executables in the entire ecosystem (`ridgunc` and `stable`), both will be written in Ridgun itself, and the project configuration file (`build.rg`) will be ordinary Ridgun code. No JSON, no YAML, no TOML, no weird inventions."* |
| 8 | |
| 9 | Let's see how to use them. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 2.1. `ridgunc`: The Sniper for Standalone Files |
| 14 | |
| 15 | When you have a single `.rg` file in front of you and want to test a quick idea, check its types, or compile an executable, you talk directly to **`ridgunc`**. |
| 16 | |
| 17 | It only has 4 commands you actually need to remember: |
| 18 | |
| 19 | ```bash |
| 20 | # 1. "Does this compile, or did I mess up a type?" |
| 21 | ridgunc check experiment.rg |
| 22 | |
| 23 | # 2. "Run it now!" (Compiles and runs in one go) |
| 24 | ridgunc run experiment.rg |
| 25 | |
| 26 | # 3. "Build me a standalone native ELF binary to go" |
| 27 | ridgunc build experiment.rg -o my_program |
| 28 | |
| 29 | # 4. "Emit the intermediate bytecode artifact (.rgc)" |
| 30 | ridgunc compile experiment.rg -o experiment.rgc |
| 31 | ``` |
| 32 | |
| 33 | ### The Hidden Superpower of `ridgunc check` |
| 34 | When you run `ridgunc check`, it doesn't just tell you *"0 errors"*. It also reveals which execution strategy the static analyzer chose for each of your functions: |
| 35 | - **`InlineSequential`**: *"This function is small and direct; I'll run it inline with zero scheduling overhead"*. |
| 36 | - **`LazyStealableTask`**: *"There is recursion or a parallel `||` graph here; I'll set it up so other CPU cores can steal work if needed"*. |
| 37 | |
| 38 | ### The Scalpel Inside `ridgunc build` (Why Your Binaries Don't Weigh 20 MB) |
| 39 | When you run `ridgunc build`, the compiler doesn't just dump all the code from every library you imported: |
| 40 | 1. It starts at your `main` function and walks the call graph to see **which functions you actually use**. If you imported a massive 80-function library but only call 2 of them, the other 78 are discarded before ever touching the assembler (*Tree-Shaking*). |
| 41 | 2. It finds every tail-recursive function and turns it into a native `loop` that consumes **0 bytes of stack**. |
| 42 | 3. It runs the final link pass with full link-time optimization (*fat LTO*) and strips DWARF debug bloat to leave you with a compact, razor-sharp binary (~350 KB). |
| 43 | |
| 44 | --- |
| 45 | |
| 46 | ## 2.2. `stable`: When Your Project Grows and You Want Order |
| 47 | |
| 48 | A standalone file is great for a script, but the moment you have multiple modules, shared libraries, and test suites, you need an orchestra conductor. That conductor is **`stable`**. |
| 49 | |
| 50 | The directory layout of a Ridgun project is completely straightforward: |
| 51 | |
| 52 | ```text |
| 53 | my-super-tool/ |
| 54 | ├── build.rg # The project manifest (it's Ridgun code!) |
| 55 | ├── src/ |
| 56 | │ ├── main/ |
| 57 | │ │ └── ridgun/ |
| 58 | │ │ ├── lib/ # Your reusable modules (`import utils`) |
| 59 | │ │ └── app/ |
| 60 | │ │ └── main.rg # Entry point (`main => ()`) |
| 61 | │ └── test/ |
| 62 | │ └── ridgun/ # Your test suites (`test *.rg`) |
| 63 | │ └── test all.rg |
| 64 | └── target/ # Where compiled binaries appear |
| 65 | ``` |
| 66 | |
| 67 | --- |
| 68 | |
| 69 | ## 2.3. `build.rg`: A Configuration File That Will Never Lie to You |
| 70 | |
| 71 | Why did we refuse to use YAML or TOML to configure a Ridgun project? |
| 72 | Because in YAML you misalign two spaces of indentation and don't find out until deployment fails. By contrast, since **`build.rg` is a Ridgun file**, it has static types, gets verified by `stable check`, and formats itself with `stable format`: |
| 73 | |
| 74 | ```ridgun |
| 75 | fn :: project name => String |
| 76 | project name = "my-super-tool" |
| 77 | |
| 78 | fn :: project version => String |
| 79 | project version = "0.1.0" |
| 80 | |
| 81 | -- "exe" (executable), "lib" (.rglib + .so library), or "both" |
| 82 | fn :: project kind => String |
| 83 | project kind = "both" |
| 84 | |
| 85 | fn :: main entry => String |
| 86 | main entry = "src/main/ridgun/app/main.rg" |
| 87 | |
| 88 | fn :: source dir => String |
| 89 | source dir = "src/main/ridgun" |
| 90 | |
| 91 | fn :: test dir => String |
| 92 | test dir = "src/test/ridgun" |
| 93 | |
| 94 | fn :: target dir => String |
| 95 | target dir = "target" |
| 96 | |
| 97 | fn :: dependencies => [String] |
| 98 | dependencies = [] |
| 99 | |
| 100 | fn :: plugins => [String] |
| 101 | plugins = [] |
| 102 | |
| 103 | fn :: task check all => String |
| 104 | task check all = "./bin/stable check && ./bin/stable test" |
| 105 | ``` |
| 106 | |
| 107 | If you mistype a field or return type, the compiler tells you immediately. Notice how all setting functions use **natural spaces** (`project name`, `project kind`, `source dir`, `task check all`) instead of underscores. |
| 108 | |
| 109 | --- |
| 110 | |
| 111 | ## 2.4. Your Daily Routine with `stable` (The Fantastic Four) |
| 112 | |
| 113 | When working inside your project, your day-to-day workflow boils down to four commands: |
| 114 | |
| 115 | ### 1. `stable format` — The End of Team Style Wars |
| 116 | ```bash |
| 117 | stable format |
| 118 | ``` |
| 119 | Your teammate puts three spaces before `=>` and you put one? Doesn't matter. Run `stable format` and in under a second it leaves **every** `.rg` file in the repository aligned like a Swiss watch. |
| 120 | |
| 121 | ### 2. `stable check` — Instant Radar |
| 122 | ```bash |
| 123 | stable check |
| 124 | ``` |
| 125 | Walks every module in `src/main/ridgun/`, verifies types, constructors, arities, and tensor dimensions, and gives you a module-by-module verdict. |
| 126 | |
| 127 | ### 3. `stable test` — Sleep Soundly at Night |
| 128 | ```bash |
| 129 | stable test |
| 130 | ``` |
| 131 | Finds all `src/test/ridgun/test *.rg` suites, executes them, and verifies every `assert equals` and `assert true`. |
| 132 | |
| 133 | ```ridgun |
| 134 | -- Example of src/test/ridgun/test math.rg |
| 135 | import test |
| 136 | |
| 137 | double Int => Int |
| 138 | double x = x * 2 |
| 139 | |
| 140 | main => () |
| 141 | main = |
| 142 | assert equals (double 21) 42 |
| 143 | assert true ((double 5) > 0) |
| 144 | ``` |
| 145 | |
| 146 | ### 4. `stable built` (and `stable clean`) — Ready for Production |
| 147 | ```bash |
| 148 | stable built |
| 149 | ``` |
| 150 | Compiles all native targets into `target/`. And if you ever want to wipe `target/` and start fresh: `stable clean`. |
| 151 | |
| 152 | --- |
| 153 | |
| 154 | ## 2.5. Building and Using Reusable Libraries (`.rglib` & ELF64 `.so`) and `stable` Plugins |
| 155 | |
| 156 | A library module does not need `main => ()`: it exports `type`, `expand type`, `fn ::`, and `op ::` declarations for other projects to import. |
| 157 | |
| 158 | ### 1. Creating a Library Project with `stable` |
| 159 | ```bash |
| 160 | stable new --lib geometry |
| 161 | cd geometry |
| 162 | stable built lib |
| 163 | ``` |
| 164 | When compiling in library mode (`project kind = "lib"` or `stable built lib`), the compiler disables `main`-rooted dead-code elimination so **all** exported functions and types are preserved, producing two artifacts in `target/`: |
| 165 | - **`target/geometry.rglib`**: a self-contained Ridgun library bundle ready to be imported from any project via `import geometry` or `import "target/geometry.rglib"`. |
| 166 | - **`target/libgeometry.so`**: a native Linux ELF64 shared object (`ET_DYN`, for `x86_64` or `arm64-linux`). |
| 167 | |
| 168 | ```ridgun |
| 169 | -- src/main/ridgun/lib/geometry.rg (No main function required!) |
| 170 | type Point = x: Int, y: Int |
| 171 | |
| 172 | fn :: shift point Point Int Int => Point |
| 173 | shift point p dx dy = Point ((x p) + dx) ((y p) + dy) |
| 174 | |
| 175 | fn :: point coords Point => Int Int |
| 176 | point coords p = (x p, y p) |
| 177 | ``` |
| 178 | |
| 179 | ### 2. How Do I Import a Module or Library? (`import`) |
| 180 | |
| 181 | Without horrific relative paths like `../../../lib/stuff.rg`. If you have a module at `src/main/ridgun/lib/geometry.rg` or a `geometry.rglib` package (in `target/`, `lib/`, or `.stable/deps/` after `stable deps`), write at the top of any file: |
| 182 | |
| 183 | ```ridgun |
| 184 | import geometry |
| 185 | ``` |
| 186 | |
| 187 | All functions, operators, and types from `geometry` become available immediately, and Ridgun's canonical loader guarantees each library is included **only once** even across diamond imports. |
| 188 | |
| 189 | ### 3. Extending `stable` with Custom Tasks (`fn :: task`) and Plugins (`plugins/*.rg`) |
| 190 | - **In `build.rg`**: declare any `fn :: task <words...> => String` (e.g., `fn :: task serve web => String`) and run it via `stable serve web`. |
| 191 | - **In `plugins/*.rg` or `.stable/plugins/*.rg`**: drop `.rg` plugin modules that contribute `fn :: task ...` commands or their own `fn :: main => ()` entrypoint, or extend `expand type StableCommand`. Inspect active extensions anytime with `stable tasks` and `stable plugins`. |
| 192 | |
| 193 | ### 4. Opt-in Strict Mode (`--strict` and `strict mode = True`) |
| 194 | By default, neither `ridgunc` nor new projects scaffolded with `stable new` or `stable init` enable strict mode. However, when you want to enforce 100% canonical space-separated Ridgun style across a project, pass `--strict` to `ridgunc` (`ridgunc check --strict`, `ridgunc build --strict`) or set `fn :: strict mode => Bool` (`strict mode = True`) in `build.rg`. Strict mode rejects with a **compilation error**: |
| 195 | 1. Any underscore `_` inside a `fn ::` or `op ::` declaration name (write `fn :: compute total`, never `fn :: compute_total`; local variable names and record field names may still use `_`). |
| 196 | 2. Any parenthesized or comma-separated multi-value return type after `=>` (write `=> Int Int` or `=> RToken [RToken]`, never `=> (Int, Int)`). |
| 197 | |
| 198 | --- |
| 199 | |
| 200 | ## 2.6. The Standard and Cryptographic Library Ecosystem (`ridgun-libs`) |
| 201 | |
| 202 | Ridgun ships with an official suite of **32 pure-Ridgun libraries** (`ridgun-libs`), compiled as `.rglib` bundles and ELF64 `.so` shared libraries ready to import via `import <name>`: |
| 203 | |
| 204 | | Family | Libraries (`import ...`) | What They Provide | |
| 205 | |---|---|---| |
| 206 | | **1. Text, Formats & Templates** | `str`, `fmt`, `regex`, `diff`, `template`, `json`, `csv`, `ini`, `codec` | String processing, tabular formatting, Thompson NFA regular expressions (`regex`), Myers diff (`diff`), logic-less templates (`template`), `json`/`csv`/`ini` parsers and serializers, and hex/Base64 encoding (`codec`). | |
| 207 | | **2. Collections, Math & Tensor Algebra** | `list`, `btree`, `math`, `tensor`, `linalg` | Functional list algorithms, ordered in-memory B-trees (`btree`), numerical routines (`math`), typed tensor views/builders (`tensor`), and linear algebra decompositions (`linalg`). | |
| 208 | | **3. OS, Filesystem & CLI** | `posix`, `io`, `path`, `glob`, `cli`, `time`, `log`, `uuid`, `test` | POSIX descriptors and signals (`posix`), deadline-aware I/O (`io`), path/glob matching (`path`, `glob`), typed CLI flags (`cli`), monotonic clocks (`time`), structured logging (`log`), RFC 4122 `uuid`, and test assertions (`test`). | |
| 209 | | **4. Cryptography & Security** | `crypto`, `argon2id`, `jwt` | Raw 32-byte binary SHA-256 (`sha256 raw` / `sha256 bytes`), HMAC-SHA-256, PBKDF2-HMAC-SHA-256, HKDF, ChaCha20-Poly1305 AEAD (`crypto`), RFC 9106 `argon2id` password hashing over `Tensor[U64 * 8 * 16]` with zero-allocation BlaMka permutations (`argon2id`), and signed tokens (`jwt`). | |
| 210 | | **5. Networking, Protocols & Agents** | `socket`, `http`, `ws`, `agent`, `pool`, `net_agent` | POSIX TCP/UDP sockets (`socket`), HTTP/1.1 client/server (`http`), RFC 6455 WebSockets (`ws`), agent supervisors (`agent`), worker pools (`pool`), and reactive agent-driven network servers (`net_agent`). | |
| 211 | |
| 212 | Now that you have the toolbox and libraries under control, let's move on to **Chapter 03** to master functions, types, blocks, and string tricks. |