Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Ridgun Language Specification |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Ridgun is a functional, parallel-first language with natural language operators and strong static typing. Its syntax draws inspiration from Haskell and ML family languages while introducing novel constructs for parallelism and human-readable operator definitions. |
| 6 | |
| 7 | Key characteristics: |
| 8 | - **Natural language operators** — Operators read like English sentences |
| 9 | - **Implicit parallelism** — Parallel execution is a syntactic primitive, not a library call |
| 10 | - **Strong static typing** — All types are declared in signatures |
| 11 | - **Pattern-style definitions** — Functions and operators are defined by equations |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## Comments |
| 16 | |
| 17 | Single-line comments use `--`: |
| 18 | |
| 19 | ```ridgun |
| 20 | -- This is a comment |
| 21 | ``` |
| 22 | |
| 23 | --- |
| 24 | |
| 25 | ## Strings |
| 26 | |
| 27 | Ridgun has two string literal types with different interpolation behavior. |
| 28 | |
| 29 | ### Interpolated Strings (double quotes) |
| 30 | |
| 31 | Double-quoted strings support **inline code interpolation** using `{expr}`. The expression inside braces is evaluated and its result is inserted into the string. |
| 32 | |
| 33 | ```ridgun |
| 34 | name = "Ridgun" |
| 35 | "Hello, {name}!" -- "Hello, Ridgun!" |
| 36 | "Sum is {sum 3 4}" -- "Sum is 7" |
| 37 | "Nested: {factorial 5} done" -- "Nested: 120 done" |
| 38 | ``` |
| 39 | |
| 40 | To include a literal brace inside `"..."`, either escape it with a backslash (`\{` and `\}`) or double it (`{{` and `}}`): |
| 41 | |
| 42 | ```ridgun |
| 43 | "Shell expansion: \$\{VAR:-default\}" -- "Shell expansion: ${VAR:-default}" |
| 44 | "This is {{not interpolated}}" -- "This is {not interpolated}" |
| 45 | "Use \{ \} or {{}} for literal braces" -- "Use { } or {} for literal braces" |
| 46 | ``` |
| 47 | |
| 48 | ### Raw Strings (single quotes) |
| 49 | |
| 50 | Single-quoted strings are **raw** — no interpolation is performed. All characters are treated as literal text, including braces. |
| 51 | |
| 52 | ```ridgun |
| 53 | 'Hello, {name}!' -- "Hello, {name}!" |
| 54 | '{this is just pure chars}' -- "{this is just pure chars}" |
| 55 | 'No {interpolation} here' -- "No {interpolation} here" |
| 56 | ``` |
| 57 | |
| 58 | ### Summary |
| 59 | |
| 60 | | Syntax | Interpolation | Use case | |
| 61 | |---|---|---| |
| 62 | | `"..."` | Yes — `{expr}` (escape braces with `\{`/`\}` or `{{`/`}}`) | Dynamic strings with embedded expressions | |
| 63 | | `'...'` | No — all literal | Static text, templates, regex patterns | |
| 64 | |
| 65 | --- |
| 66 | |
| 67 | ## Program Structure |
| 68 | |
| 69 | All executable code in Ridgun must live inside a function or operator. The `main` function is the **required entry point** for program execution. |
| 70 | |
| 71 | ```ridgun |
| 72 | fn :: main => () |
| 73 | main = echo "Hello, Ridgun!" |
| 74 | ``` |
| 75 | |
| 76 | A Ridgun source file may contain only: |
| 77 | - `import` statements |
| 78 | - `type` definitions |
| 79 | - `fn` and `op` declarations and definitions |
| 80 | - A `main` function as the entry point |
| 81 | |
| 82 | No loose top-level expressions are allowed. Everything that runs must be called from `main`. |
| 83 | |
| 84 | ### CLI Usage |
| 85 | |
| 86 | ``` |
| 87 | ridgunc <command> [options] <file.rg> |
| 88 | ``` |
| 89 | |
| 90 | | Command | Status | Purpose | |
| 91 | |---|---|---| |
| 92 | | `run <file.rg>` | ✅ Active | Compile and execute a Ridgun source file | |
| 93 | | `check <file.rg>` | ✅ Active | Type-check and analyze static strategies without running | |
| 94 | | `compile <file.rg> [-o out.rgc]` | ✅ Active | Emit `.rgc` bytecode artifact | |
| 95 | | `build <file.rg> [-o binary] [--target arch]` | ✅ Active | Build standalone native ELF64 executable | |
| 96 | | `build --lib <file.rg> [-o out.rglib]` | ✅ Active | Build reusable Ridgun library (`.rglib` & `.so`) | |
| 97 | | `build --so <file.rg> [-o out.so]` | ✅ Active | Build native ELF64 shared library (`ET_DYN` `.so`) | |
| 98 | | `version` / `--version` | ✅ Active | Show compiler version and target architecture | |
| 99 | | `help` / `--help` | ✅ Active | Show usage information | |
| 100 | |
| 101 | Common options include `--strict` / `-S` (enforce strict canonical Ridgun syntax rules) and `--target x86_64-linux` / `arm64-linux`. Project-level workflows (`build`, `test`, `check`, `format`, `deps`, interactive REPL) are provided by `stable`. |
| 102 | |
| 103 | --- |
| 104 | |
| 105 | ## Functions & Operators |
| 106 | |
| 107 | Functions and natural-language/infix operators are declared by a type signature ending in `=> <return_type>`, followed by one or more equations (`cases`). The leading `fn ::` and `op ::` prefixes are **optional**: |
| 108 | |
| 109 | - **Canonical prefixless form** (starts at column 0): |
| 110 | ```ridgun |
| 111 | list len [A] => Int |
| 112 | list len [] = 0 |
| 113 | list len (_:xs) = 1 + (list len xs) |
| 114 | |
| 115 | swap A with B => (B, A) |
| 116 | swap x with y = (y, x) |
| 117 | ``` |
| 118 | - **Explicit prefixed form** (`fn ::` / `op ::`, starts at column 0 with clauses aligned at column 6): |
| 119 | ```ridgun |
| 120 | fn :: sum Int Int => Int |
| 121 | sum a b = a + b |
| 122 | |
| 123 | fn :: format app err AppErr => String |
| 124 | format app err (IoError msg) = "IO:{msg}" |
| 125 | ``` |
| 126 | - **Natural-language spaces in `fn ::`, built-ins, and filenames (`FR-COMP-005`)**: |
| 127 | - `fn ::` declarations and calls support multi-word names separated by spaces (`fn :: format app err AppErr => String`, `fn :: project name => String`), just like `op ::` and prefixless declarations. |
| 128 | - Built-in runtime and test functions can be called with spaces (`assert equals a b`, `str len s`, `read file p`, `cmm live blocks`) while keeping `_` forms backward-compatible. |
| 129 | - First-class multi-word function references (`map double val xs`, `||map double val xs`, `filter is even xs`, `fold add pair 0 xs`) and `.rg` filenames with spaces (`src/test/ridgun/test core language.rg`) are supported natively by `ridgunc` and `stable`. |
| 130 | |
| 131 | ### Polymorphic Type Variables (`A`..`Z`) & Contextual Keywords (`type`, `with`) |
| 132 | |
| 133 | To guarantee zero lexical ambiguity between operator words and type parameters in both prefixed (`op ::`) and prefixless signatures: |
| 134 | - **Every type token in a signature starts with an uppercase letter** (or `[...]` / `(...)`): |
| 135 | - **Single uppercase letters (`A`..`Z`)** denote **polymorphic type variables** (e.g., `[A]`, `(A, B)`, `(A => B)`, `swap A with B => (B, A)`). Repeated occurrences of a type variable across parameters are unified at each call site and substituted into the return type. |
| 136 | - **Multi-character capitalized identifiers** (`Int`, `String`, `Bool`, `RExpr`, `Tensor`) denote concrete or user-defined types. |
| 137 | - **Every lowercase token** (or operator symbol `+`, `-`, `*`, `/`, `%`) in a top-level signature is part of the function or mixfix operator name. |
| 138 | - **Contextual keywords `type` and `with`**: `type` only introduces a type declaration at the top level when followed by `UpperName =`. Everywhere else (`fn ::` and `op ::` signatures, operator words, parameter patterns, local variables, and record field names), `type` and `with` are valid identifiers/words without ambiguity. |
| 139 | - A top-level signature with **one leading lowercase identifier** followed only by types before `=>` is a prefix function (`fn`); any signature with **two or more lowercase words** or operator symbols interleaved with types before `=>` is a mixfix/infix/postfix operator (`op`). |
| 140 | |
| 141 | The declaration and definition are separate: |
| 142 | - **Declaration** (`fn ::`) — specifies the name, parameter types, and return type |
| 143 | - **Definition** — binds parameter names and provides the implementation |
| 144 | |
| 145 | --- |
| 146 | |
| 147 | ## Operators |
| 148 | |
| 149 | Operators are Ridgun's distinctive feature. They are **named with natural language** and defined with `op ::`. Unlike traditional operators (`+`, `-`), Ridgun operators are multi-word expressions that read like sentences. |
| 150 | |
| 151 | ### Syntax |
| 152 | |
| 153 | ``` |
| 154 | op :: <natural language signature with types> -> <return_type> |
| 155 | <natural language pattern with params> = <body> |
| 156 | ``` |
| 157 | |
| 158 | ### Basic Example |
| 159 | |
| 160 | ```ridgun |
| 161 | -- Declaration: "add [Int] to [Int]" returns an Int |
| 162 | op :: add Int to Int => Int |
| 163 | add a to b = a + b |
| 164 | ``` |
| 165 | |
| 166 | Usage would read as: `add 3 to 5` → `8` |
| 167 | |
| 168 | The words between typed parameters (`to`, `and`, `in`, etc.) are **keywords of the operator** — they are part of the operator's identity and must appear at call sites. |
| 169 | |
| 170 | ### Parallel Operators (`||`) |
| 171 | |
| 172 | Operators can express **parallel execution** as a first-class concept using `||`: |
| 173 | |
| 174 | ```ridgun |
| 175 | -- Run two configs in parallel with named DAG bindings (|| expr -> var) |
| 176 | op :: run Conf and Conf in parallel => Res, Res |
| 177 | run a and b in parallel = res_a, res_b |
| 178 | || run my own process a -> res_a |
| 179 | || run my own process b -> res_b |
| 180 | ``` |
| 181 | |
| 182 | Key elements: |
| 183 | - **Multiple return values** — `=> Res, Res` declares two return values |
| 184 | - **`|| expr -> var` block** — Binds sub-computations to result names (`where` is eliminated; `||` unifies all parallel branches) |
| 185 | - **`->`** — Assigns the result of a parallel sub-computation to a variable (or tensor slice `-> out[i, j]`) |
| 186 | - Independent `||` branches execute concurrently as a parallel DAG based on data dependencies |
| 187 | |
| 188 | ### Direct Parallel Pipe Block (`|| expr`) |
| 189 | |
| 190 | When an expression or function directly returns a tuple of parallel computations without intermediate variable names, `||` lines appear directly after `=`: |
| 191 | |
| 192 | ```ridgun |
| 193 | -- Run two processes in parallel and return their results as a tuple |
| 194 | op :: another way to run two processes Conf Conf in parallel => Res, Res |
| 195 | another way to run two processes a b in parallel = |
| 196 | || run my own process a |
| 197 | || run my own process b |
| 198 | ``` |
| 199 | |
| 200 | Each `||` line runs as an independent parallel task. Results are collected in declaration order. |
| 201 | |
| 202 | ### Operator Overloading |
| 203 | |
| 204 | Existing operators (including built-in ones like `+`) can be overloaded for new types: |
| 205 | |
| 206 | ```ridgun |
| 207 | -- Overload + for Complex numbers |
| 208 | op :: Complex + Complex => Complex |
| 209 | a + b = (real a) + (real b) + complex_i * ((imaginary a) + (imaginary b)) |
| 210 | ``` |
| 211 | |
| 212 | ### Precedence |
| 213 | |
| 214 | **Symbolic operators have the highest precedence** — math first. |
| 215 | |
| 216 | When symbolic operators (like `+`, `*`) appear inside natural language operator bodies, they bind tighter than the surrounding operator keywords. This ensures arithmetic expressions evaluate as expected without extra parentheses. |
| 217 | |
| 218 | | Precedence | Category | Examples | |
| 219 | |---|---|---| |
| 220 | | **Highest** | Symbolic operators | `*`, `/`, `+`, `-`, `%` | |
| 221 | | | Function application | `f x` | |
| 222 | | | Comparison | `==`, `!=`, `<`, `>` | |
| 223 | | | Logical | `and`, `or`, `xor`, `not` | |
| 224 | | **Lowest** | Natural language operators | `add a to b`, `run a and b in parallel` | |
| 225 | |
| 226 | ### Operator Chaining & Idiomatic Left-to-Right Pipelines (`->`) (`FR-COMP-014`) |
| 227 | |
| 228 | Operators and functions that transform values or sequences can be **chained left-to-right** so data flows naturally from source to destination without nested right-to-left parentheses (`h (g (f x))`) or disposable intermediate variables (`st1`, `st2`, `st3`). |
| 229 | |
| 230 | #### 1. The Recommended Idiomatic Ridgun Pipeline Style (`2b`) |
| 231 | |
| 232 | Ridgun unifies infix left-operand invocation and explicit `->` pipeline stages into the same AST. While all four forms below are valid and equivalent, **Form `2b`** (starting naturally with a subject + operation on the left, chaining subsequent transformations with `->`, and binding the final result with `-> target`) is the **recommended idiomatic Ridgun style**: |
| 233 | |
| 234 | ```ridgun |
| 235 | -- Recommended idiomatic style (2b): |
| 236 | xs map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results |
| 237 | |
| 238 | -- Equivalent unified forms (all normalize to the exact same AST): |
| 239 | xs -> map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results -- (2) explicit -> from subject |
| 240 | xs map (x => x * 2) filter (x => x > 6) compact limit 14 -> results -- (1) contiguous infix chain |
| 241 | map (x => x * 2) xs -> filter (x => x > 6) -> compact limit 14 -> results -- (3) prefix start + -> chain |
| 242 | ``` |
| 243 | |
| 244 | Multi-stage pipelines can also be written across indented continuation lines starting with `->`: |
| 245 | |
| 246 | ```ridgun |
| 247 | raw_nums map (x => x * 3) |
| 248 | -> filter (x => x >= 12) |
| 249 | -> fold 0 (acc x => acc + x) |
| 250 | -> total_sum |
| 251 | ``` |
| 252 | |
| 253 | #### 2. Universal `->` First-Argument Rule & Collection Combinators |
| 254 | |
| 255 | - **Universal Rule for Functions & Operators**: Any value coming from the left of `->` (or left of an infix call `x op a b`) enters as the **first argument** of the right-hand function or operator: |
| 256 | - `x -> f a b` $\equiv$ `x f a b` $\equiv$ `f x a b` |
| 257 | - Works seamlessly for both `fn ::` declarations and `op ::` mixfix declarations without duplicating signatures. |
| 258 | - If a downstream function needs the piped value at a non-first parameter position, an explicit `_` placeholder places the piped value at `_` (`payload -> write file output_path _`). |
| 259 | - **Collection Combinators (`map`, `||map`, `filter`, `||filter`, `fold`)**: Accept the collection either in traditional prefix position (`map f xs`, `filter g xs`, `fold f init xs`) or from the left of an infix/pipeline stage (`xs map f`, `xs -> map f`, `xs filter g`, `xs -> filter g`, `xs fold init f`, `xs -> fold init f`). |
| 260 | - **Agent Stages (`-> send to`, `-> ask to`)**: Piping into `-> send to ag` or `-> ask to ag` passes the left value as the message (`[5, 10, 15] -> sum int list -> send to worker_ag`). |
| 261 | - **Terminal Variable Binding (`-> var`)**: When a pipeline statement in a sequential block ends with `-> var` (where `var` is a local variable name rather than a declared function), it binds `var` (`var = <pipeline_result>`) for the remainder of the block. |
| 262 | |
| 263 | #### 3. Sequence Masks in Pipelines (`-> (+ _ _ +) ->`) |
| 264 | |
| 265 | When a stage returns a multi-value sequence or tuple, a sequence mask stage (`-> (+ _ _ +) ->`) filters its elements (`+` keeps a position, `_` discards it) and unpacks the selected tuple directly into a multi-argument downstream function: |
| 266 | |
| 267 | ```ridgun |
| 268 | parse header packet "v0.9.9" -> (+ _ _ +) -> combine code msg -> status_line |
| 269 | ``` |
| 270 | |
| 271 | #### 4. `_ =`-Free Side-Effect Statements & Explicit Unit Discard (`-> ()`) |
| 272 | |
| 273 | In sequential blocks, side-effect statements (`echo`, `write file`, `write bytes`, `shell exec`, `log info`, `print error lines`, `sys mkdir`, etc.) are written directly **without `_ =` boilerplate**: |
| 274 | |
| 275 | ```ridgun |
| 276 | verify strict if enabled strict_mode input_path |
| 277 | s_errs -> format type errors -> print error lines |
| 278 | echo "[ridgunc --strict] {input_path}: {err_c} error(s) found." |
| 279 | sys exit 1 |
| 280 | ``` |
| 281 | |
| 282 | When the **final expression** of a `=> ()` function calls a helper that returns a non-`()` value (such as `shell exec : String => Int`), append `-> ()` to evaluate the expression, discard its return value, and yield `()`: |
| 283 | |
| 284 | ```ridgun |
| 285 | fn :: exec built run Int => () |
| 286 | exec built run 0 = shell exec "./target/app" -> () |
| 287 | exec built run _ = sys exit 1 |
| 288 | ``` |
| 289 | |
| 290 | #### 5. Point-Free Sequence Operator Chaining |
| 291 | |
| 292 | Operators that return sequences can also be chained point-free: |
| 293 | |
| 294 | ```ridgun |
| 295 | op :: Int Int switch -> Int Int |
| 296 | a b switch = b a |
| 297 | |
| 298 | op :: Int Int invariant -> Int Int |
| 299 | a b invariant = a b switch switch |
| 300 | ``` |
| 301 | |
| 302 | > **Note:** `->` and `=>` are both accepted as return type separators in `fn ::` / `op ::` headers. In expressions, `->` is the left-to-right pipeline and binding operator, while `=>` introduces lexical closures `(x => expr)`. |
| 303 | |
| 304 | --- |
| 305 | |
| 306 | ## Type System |
| 307 | |
| 308 | ### Type Signatures |
| 309 | |
| 310 | Type signatures appear in declarations after `fn ::` or `op ::` (or without a prefix): |
| 311 | |
| 312 | ``` |
| 313 | fn :: <name> <type1> <type2> ... => <return_type1> <return_type2> ... |
| 314 | op :: <words and types mixed> => <return_type1> <return_type2> ... |
| 315 | ``` |
| 316 | |
| 317 | - Parameters are positional; types appear where parameters are expected. |
| 318 | - **Unified Tuple and Sequence Type Syntax (`FR-COMP-008`)**: |
| 319 | - In Ridgun, an argument list `T1 T2 ... Tn` and a tuple `(T1, T2, ..., Tn)` represent the same underlying compound type: an ordered, fixed-size sequence of typed values (`seq`). |
| 320 | - Therefore, space-separated types and comma-separated tuple types are **100% interchangeable** across return types, list brackets, and parenthesized tuples: |
| 321 | - **Return types after `=>` / `->`**: |
| 322 | ```ridgun |
| 323 | fn :: parse header String => Int Int [String String] String |
| 324 | -- 100% equivalent to: |
| 325 | fn :: parse header String => (Int, Int, [(String, String)], String) |
| 326 | ``` |
| 327 | - **Lists of tuples (`[T1 T2 ... Tn]`)**: |
| 328 | ```ridgun |
| 329 | [String String] -- equivalent to [(String, String)] |
| 330 | [Int String Bool] -- equivalent to [(Int, String, Bool)] |
| 331 | ``` |
| 332 | - **Parenthesized tuples (`(T1 T2 ... Tn)`)**: |
| 333 | ```ridgun |
| 334 | fn :: fst (A B) => A |
| 335 | fst (a, _) = a |
| 336 | ``` |
| 337 | - **Why this has zero syntactic ambiguity**: Unlike Haskell/ML (where juxtaposition `Maybe Int` denotes type application), parameterized types in Ridgun use explicit parentheses (`Tensor(Float, 3, 3)`) and lists use brackets (`[T]`). Moreover, every type token starts with an uppercase letter (`A`..`Z`, `Int`, `String`), `[`, or `(`, while function and operator words are lowercase. Thus, `T1 T2` never conflicts with type constructors or operator keywords. |
| 338 | - The `=>` (or `->`) arrow separates input types from the return type. |
| 339 | |
| 340 | ### Built-in Types |
| 341 | |
| 342 | | Type | Description | |
| 343 | |---|---| |
| 344 | | `Int` | 64-bit signed integer (`-9223372036854775808 .. 9223372036854775807`) | |
| 345 | | `Real` | Real number | |
| 346 | | `Bool` | Boolean | |
| 347 | | `Char` | Single Unicode character (UTF-8) | |
| 348 | | `String` | Text string (UTF-8) | |
| 349 | | `Complex` | Complex number | |
| 350 | | `Tensor` | Mutable N-dimensional tensor (`Real` elements, CPU) | |
| 351 | | `Tensor_GPU` | Mutable N-dimensional tensor (`Real` elements, GPU) | |
| 352 | |
| 353 | > [!NOTE] |
| 354 | > All text in Ridgun is UTF-8 encoded by default. `Char` represents a single Unicode codepoint, `String` is a sequence of `Char`. |
| 355 | |
| 356 | ### Int Literals (`FR-COMP-016`) |
| 357 | |
| 358 | - A decimal literal (`42`, `1_000_000`, `64_KB`) must lie in `0 .. 9223372036854775807` after its unit suffix is applied. A larger literal is a compile error: `integer literal '<text>' is out of range for Int (-9223372036854775808 .. 9223372036854775807)`. |
| 359 | - `9223372036854775808` is valid only after a unary minus: `-9223372036854775808` is the `Int` minimum. `0 - 9223372036854775808` is the same compile error. |
| 360 | - A hexadecimal literal (`0xFF00`, `0x7FFF_FFFF_FFFF_FFFF`) is the 64-bit two's-complement bit pattern of its digits: `0x8000000000000000` is the `Int` minimum and `0xFFFFFFFFFFFFFFFF` is `-1`. More than 16 significant hex digits is a compile error. |
| 361 | - `str_to_int` returns the exact 64-bit value. It ignores leading and trailing ASCII whitespace and every `_`, accepts one `+` or `-` sign (after the prefix for `0x`), and returns `0` for text that is not a number or does not fit in `Int`. |
| 362 | |
| 363 | ### Bool Literals |
| 364 | |
| 365 | `Bool` has two primitive values: |
| 366 | |
| 367 | ```ridgun |
| 368 | True -- boolean true |
| 369 | False -- boolean false |
| 370 | ``` |
| 371 | |
| 372 | `True` and `False` are constructors — they can be used in expressions, passed as arguments, and pattern-matched: |
| 373 | |
| 374 | ```ridgun |
| 375 | fn :: is valid Int => Bool |
| 376 | is valid n = |
| 377 | | n > 0 = True |
| 378 | | otherwise = False |
| 379 | ``` |
| 380 | |
| 381 | ### Logical Operators |
| 382 | |
| 383 | Ridgun uses **word-based lowercase** logical operators instead of symbolic ones: |
| 384 | |
| 385 | | Operator | Meaning | Example | |
| 386 | |---|---|---| |
| 387 | | `and` | Logical conjunction | `a and b` | |
| 388 | | `or` | Logical disjunction | `a or b` | |
| 389 | | `xor` | Exclusive or | `a xor b` | |
| 390 | | `not` | Logical negation | `not a` | |
| 391 | |
| 392 | ```ridgun |
| 393 | result = True and False -- False |
| 394 | result = True or False -- True |
| 395 | result = True xor True -- False |
| 396 | result = not False -- True |
| 397 | ``` |
| 398 | |
| 399 | These are infix keyword operators with standard boolean semantics. |
| 400 | |
| 401 | ### Native Collection Types |
| 402 | |
| 403 | #### Lists |
| 404 | |
| 405 | Lists are a built-in **immutable** recursive type with dedicated syntax. No manual ADT definition is needed. Lists are suited for functional data processing, symbolic computation, and recursive algorithms. For mutable numeric computation, see **Tensors** below. |
| 406 | |
| 407 | **Type syntax:** `[type]` — a list of elements of the given type. |
| 408 | |
| 409 | ```ridgun |
| 410 | fn :: sum list [Int] => Int |
| 411 | fn :: reverse [Int] => [Int] |
| 412 | ``` |
| 413 | |
| 414 | **Literal syntax:** `[elem1, elem2, ...]` — a list literal. `[]` is the empty list. |
| 415 | |
| 416 | ```ridgun |
| 417 | fn :: my list => [Int] |
| 418 | my list = [1, 2, 3, 4, 5] |
| 419 | ``` |
| 420 | |
| 421 | **Cons-pattern matching:** `(x:xs)` — destructures a list into its head `x` and tail `xs`. `[]` matches the empty list. Use `_` as a wildcard that matches but does not bind. |
| 422 | |
| 423 | ```ridgun |
| 424 | fn :: sum list [Int] => Int |
| 425 | sum list [] = 0 |
| 426 | sum list (x:xs) = x + sum list xs |
| 427 | |
| 428 | fn :: list length [Int] => Int |
| 429 | list length [] = 0 |
| 430 | list length (_:xs) = 1 + list length xs |
| 431 | ``` |
| 432 | |
| 433 | **Concatenation:** `::` joins two lists (or sequences). |
| 434 | |
| 435 | ```ridgun |
| 436 | fn :: append [Int] [Int] => [Int] |
| 437 | append a b = a :: b |
| 438 | ``` |
| 439 | |
| 440 | #### Tensors |
| 441 | |
| 442 | Tensors are Ridgun's native **mutable** N-dimensional numeric type. They generalize vectors, matrices, and higher-dimensional arrays into a single type. Elements are `Real` by default. |
| 443 | |
| 444 | **Literal syntax:** `Tensor` followed by nested brackets. The nesting depth determines the dimensionality. |
| 445 | |
| 446 | ```ridgun |
| 447 | v = Tensor [1, 2, 3] -- 1D (vector), shape: (3) |
| 448 | m = Tensor [[1, 2], [5, 3]] -- 2D (matrix), shape: (2, 2) |
| 449 | t = Tensor [[[1, 2], [3, 4]], -- 3D tensor, shape: (2, 2, 2) |
| 450 | [[5, 6], [7, 8]]] |
| 451 | ``` |
| 452 | |
| 453 | **Indexing:** Both chained and comma-separated indexing are supported. |
| 454 | |
| 455 | ```ridgun |
| 456 | val = m[0][1] -- chained indexing: row 0, col 1 |
| 457 | val = m[0, 1] -- comma indexing: equivalent |
| 458 | row = m[0] -- slice: entire first row |
| 459 | ``` |
| 460 | |
| 461 | **Mutation (in-place):** Tensors are mutable — elements and slices can be modified in place (PyTorch/NumPy style). |
| 462 | |
| 463 | ```ridgun |
| 464 | m[0, 1] = 5.0 -- set element |
| 465 | m[0] = Tensor [10, 20] -- set entire row |
| 466 | ``` |
| 467 | |
| 468 | **Tensor contraction (`@`):** The `@` operator performs Einstein summation. Subscript indices on each operand define the contraction: indices shared across **all** operands are summed; unique indices survive in the result. |
| 469 | |
| 470 | ```ridgun |
| 471 | -- A has shape (s, d, f), B has shape (n, d, f) |
| 472 | -- Shared indices: d, f → contracted (summed) |
| 473 | -- Result T has shape (s, n) |
| 474 | T = A_sdf @ B_ndf |
| 475 | |
| 476 | -- Equivalent to: np.einsum('sdf,ndf->sn', A, B) |
| 477 | ``` |
| 478 | |
| 479 | Contraction works across **multiple tensors** in a single expression: |
| 480 | |
| 481 | ```ridgun |
| 482 | -- A(a,b,c), B(a,b,c), C(a,b,c,d) |
| 483 | -- Shared: a, b, c → contracted. Result R has shape (d) |
| 484 | R = A_abc @ B_abc @ C_abcd |
| 485 | |
| 486 | -- Equivalent to: np.einsum('abc,abc,abcd->d', A, B, C) |
| 487 | ``` |
| 488 | |
| 489 | **Hadamard operations (`+`, `-`, `*`, `/`):** Arithmetic operators with subscript indices perform **element-wise** operations. Matching indices align dimensions; unmatched indices are broadcast. |
| 490 | |
| 491 | ```ridgun |
| 492 | -- Element-wise multiply: same shape, same indices |
| 493 | H = A_ij * B_ij -- shape (i, j) |
| 494 | |
| 495 | -- Broadcasting: B has fewer dimensions |
| 496 | S = A_ij * B_j -- B broadcasts along i |
| 497 | |
| 498 | -- Element-wise add |
| 499 | C = A_ij + B_ij |
| 500 | |
| 501 | -- Element-wise divide |
| 502 | D = A_ij / B_j -- B broadcasts along i |
| 503 | ``` |
| 504 | |
| 505 | This eliminates the need for `unsqueeze`, `expand_dims`, or manual reshaping — the indices **are** the broadcasting rules. |
| 506 | |
| 507 | **GPU variant:** `Tensor_GPU` places the tensor on GPU memory for accelerated computation. |
| 508 | |
| 509 | ```ridgun |
| 510 | g = Tensor_GPU [[1, 2], [3, 4]] |
| 511 | result = g_ij @ g_jk -- GPU-accelerated matrix multiply |
| 512 | ``` |
| 513 | |
| 514 | #### Sequences (`seq`) |
| 515 | |
| 516 | Sequences are Ridgun's core compound type. A `seq` is an ordered, fixed-size collection of values — essentially a typed tuple. Function and operator parameter lists are implicitly sequences. |
| 517 | |
| 518 | ```ridgun |
| 519 | -- These parameter types form implicit sequences: |
| 520 | fn :: sum Int Int => Int -- params: seq(Int, Int) |
| 521 | fn :: process Int Real => Bool -- params: seq(Int, Real) |
| 522 | ``` |
| 523 | |
| 524 | **Seq binding (`(name)`)** — wrapping a parameter name in parentheses binds all parameters as a single seq variable: |
| 525 | |
| 526 | ```ridgun |
| 527 | fn :: first of seq Int Int => Int |
| 528 | first of seq (s) = s(+ _) |
| 529 | |
| 530 | -- Equivalent without seq binding: |
| 531 | fn :: first of Int Int => Int |
| 532 | first of s _ => s |
| 533 | ``` |
| 534 | |
| 535 | **Mask selector (`s(+ _)`)** — extracts elements by mask, where `+` selects a position and `_` skips it: |
| 536 | |
| 537 | ```ridgun |
| 538 | s(+ _) -- select 1st, skip 2nd |
| 539 | s(_ +) -- skip 1st, select 2nd |
| 540 | s(+ _ _ _) -- select 1st, skip 2nd-4th |
| 541 | s(_ _ _ +) -- skip 1st-3rd, select 4th |
| 542 | ``` |
| 543 | |
| 544 | **Index selector (`s(0 3)`)** — extracts elements by their zero-based positions: |
| 545 | |
| 546 | ```ridgun |
| 547 | fn :: extreme points Int Int Int Int => Int Int |
| 548 | extreme points (s) = s(0 3) |
| 549 | ``` |
| 550 | |
| 551 | **Flat join (`:`)** — concatenates two sequences into a single flat sequence: |
| 552 | |
| 553 | ```ridgun |
| 554 | seq1 = a, b -- seq(a, b) |
| 555 | seq2 = c, d -- seq(c, d) |
| 556 | seq1:seq2 -- seq(a, b, c, d) |
| 557 | ``` |
| 558 | |
| 559 | **Nesting (`,`)** — composes sequences as distinct elements (seq of seqs): |
| 560 | |
| 561 | ```ridgun |
| 562 | seq1, seq2 -- seq(seq(a, b), seq(c, d)) |
| 563 | ``` |
| 564 | |
| 565 | ### Algebraic Data Types (ADTs) |
| 566 | |
| 567 | Ridgun uses **algebraic data types** for user-defined types. There are no classes, methods, or `impl` blocks — functions and operators take types as parameters. |
| 568 | |
| 569 | #### Product Types (records) & Record Update Syntax (`with`) |
| 570 | |
| 571 | A type with named fields: |
| 572 | |
| 573 | ```ridgun |
| 574 | type Conf = host: String, port: Int, tls: Bool |
| 575 | type Point = x: Real, y: Real |
| 576 | ``` |
| 577 | |
| 578 | Fields are accessed by name as functions (`host c`, `port c`), and updated functionally using `with` and `=`: |
| 579 | |
| 580 | ```ridgun |
| 581 | fn :: get host Conf => String |
| 582 | get host c = host c |
| 583 | |
| 584 | -- Functional record update (creates a new Conf with updated fields; |
| 585 | -- mutates in place via Arc::try_unwrap when the record is uniquely owned): |
| 586 | fn :: enable tls Conf => Conf |
| 587 | enable tls c = c with tls = True, port = 443 |
| 588 | ``` |
| 589 | |
| 590 | #### Sum Types (variants) |
| 591 | |
| 592 | A type with multiple constructors, separated by `|`: |
| 593 | |
| 594 | ```ridgun |
| 595 | type Result = Ok Int | Err String |
| 596 | type Option = Some Int | None |
| 597 | type Color = Red | Green | Blue |
| 598 | ``` |
| 599 | |
| 600 | #### Generic Sum ADTs & Generic Product Records (`FR-COMP-013`) |
| 601 | |
| 602 | User-defined Sum ADTs and Product Records may declare one or more uppercase type parameters (`A`..`Z` or capitalized type variable names) between the type name and `=`: |
| 603 | |
| 604 | ```ridgun |
| 605 | type Result T E = Ok T | Err E |
| 606 | type Option A = Some A | None |
| 607 | type Box A = Box A |
| 608 | type Page T = items: [T], total: Int |
| 609 | type Pair A B = fst: A, snd: B |
| 610 | ``` |
| 611 | |
| 612 | - Parametric type applications (`Result Int String`, `Page User`, `[Box Int]`) can be used in `fn ::` / `op ::` signatures, nested record fields, and ADT constructor payloads. |
| 613 | - Constructors (`Ok 42`, `Ok "hello"`, `Page [1, 2] 2`), pattern matching, field accessors (`items p`, `total p`), and functional record updates (`p with total = 10`) instantiate fresh type variables at each call site. |
| 614 | - `type Option A = Some A | None` is pre-registered as a built-in polymorphic ADT when not already declared by the user, so `try receive from ag` returns `Some v` or `None` without requiring boilerplate declarations (`FR-COMP-013`). |
| 615 | |
| 616 | #### Anonymous Functions & Lexical Closures (`(x => expr)`, `(a b => expr)` — `FR-COMP-013`) |
| 617 | |
| 618 | Anonymous functions (lambdas) are written inside parentheses with space-separated parameters followed by `=>` and the body expression (`(x => expr)` or `(a b => expr)`, e.g. `map (x => x + offset) xs`, `fold (acc x => acc + x) 0 xs`) and may capture local variables or parameters from their enclosing lexical scope: |
| 619 | |
| 620 | ```ridgun |
| 621 | fn :: add offset Int [Int] => [Int] |
| 622 | add offset k xs = map (x => x + k) xs |
| 623 | ``` |
| 624 | |
| 625 | Five canonical rules govern anonymous functions: |
| 626 | |
| 627 | 1. **Mandatory parentheses**: Every lambda must be enclosed in parentheses `(... => ...)`. Unparenthesized `x => expr` (such as `f = x => x + 1`) is a compile-time error (`lambda expression must be enclosed in parentheses: '(x => expr)'`). |
| 628 | 2. **Zero-parameter lambdas (`(=> expr)`)**: Ridgun forbids `()` before `=>` in function signatures (`fn :: main => ()`). Consistently, a zero-parameter lambda is written `(=> expr)` (invoked as `thunk ()`), and its type in signatures is `(=> Type)`. Writing `(() => expr)` or `(() => Type)` is a compile-time error. |
| 629 | 3. **Multiple parameters vs. tuple destructuring (`(a b => expr)` vs. `((a b) => expr)`)**: `(a b => expr)` is a 2-parameter lambda (such as `fold (acc x => acc + x) 0 xs`), whereas `((a b) => expr)` is a 1-parameter lambda that destructures a tuple (`map ((a b) => a + b) [(1, 2), (3, 4)]`). Mixing them up produces a dedicated type error explaining the difference. |
| 630 | 4. **Multi-line lambda bodies**: When `=>` is at the end of the line, the indented block underneath forms a multi-statement (or `|` guard) body closed by `)`: |
| 631 | ```ridgun |
| 632 | map (x => |
| 633 | doubled = x * 2 |
| 634 | doubled + offset |
| 635 | ) [1, 2, 3] |
| 636 | ``` |
| 637 | 5. **Uppercase types vs. lowercase values**: `(Conn => Conn)` is a function type and `(conn => conn)` is a lambda value. Writing uppercase types `(Conn => Conn)` in an expression or lowercase parameters `(conn => conn)` in a `fn ::` signature is rejected with a clear compile-time error. |
| 638 | |
| 639 | Captured bindings are lifted automatically into partial-application closures managed by `RMM1`/`RMM2`. |
| 640 | |
| 641 | #### Recursive Types |
| 642 | |
| 643 | Types can reference themselves: |
| 644 | |
| 645 | ```ridgun |
| 646 | type List = Empty | Cons Int List |
| 647 | type Tree = Leaf Int | Node Tree Tree |
| 648 | ``` |
| 649 | |
| 650 | #### Shared Constructor Names Across Types (`FR-COMP-005`) |
| 651 | |
| 652 | Multiple `type` declarations can reuse the same variant constructor names (such as `Error`, `Var`, `Lit`, `None`) with the same or different arities. The typechecker disambiguates constructor patterns via the expected parameter type of the column, and resolves constructor calls by argument arity/types and union ADT return types: |
| 653 | |
| 654 | ```ridgun |
| 655 | type ParseOut = Parsed Int | Error String |
| 656 | type DbOut = Rows Int | Error Int String |
| 657 | type MiniExpr = Var String | Lit Int |
| 658 | type MiniPat = Var String | Lit Int | Wild |
| 659 | ``` |
| 660 | |
| 661 | #### Open Type Extensions (`expand type`) & Automatic `fn ::` Clause Merging (`FR-COMP-005`) |
| 662 | |
| 663 | Existing Sum ADTs and Product Records can be extended with `expand type UpperName = ...`: |
| 664 | |
| 665 | - **Extending a Sum ADT**: appends new variant constructors to the existing type. |
| 666 | - **Extending a Product Record**: appends new named fields (`field: Type`) and auto-generates their field accessor functions. |
| 667 | - **Type-Name Variants Without Redundant Constructor Names**: in `type` and `expand type` sum declarations, a variant whose name is a bare built-in type (`String`, `Int`, `Real`/`Float`, `Bool`, `Char`) with no extra type arguments after it represents values of that type directly (without writing `Int Int` or `String String`). Callers can pass bare values (`describe "hej"`, `describe 42`) or explicit constructor calls (`describe (String "hej")`, `describe (Int 42)`), and equations can match both literals (`"admin"`, `0`) and typed patterns (`(String s)`, `(Int n)`). |
| 668 | - **Automatic `fn ::` / `op ::` Clause Merging (without `expand fn`)**: re-declaring an existing `fn ::` or `op ::` with new pattern clauses automatically merges those clauses into the existing function definition, placing specific constructor/literal pattern clauses **before** any catch-all (`_` / variable) fallback clause: |
| 669 | |
| 670 | ```ridgun |
| 671 | type Something = String | Int | MyStruct Float Int |
| 672 | expand type Something = Bool |
| 673 | |
| 674 | fn :: describe something Something => String |
| 675 | describe something "admin" = "special_admin" |
| 676 | describe something 0 = "zero_int" |
| 677 | describe something (String s) = "str:{s}" |
| 678 | describe something (Int n) = "int:{n}" |
| 679 | describe something (Bool b) = "bool:{b}" |
| 680 | describe something (MyStruct ratio cnt) = "struct:{ratio}:{cnt}" |
| 681 | ``` |
| 682 | |
| 683 | ```ridgun |
| 684 | type AppErr = IoError String | Timeout Int |
| 685 | |
| 686 | fn :: format app err AppErr => String |
| 687 | format app err (IoError msg) = "IO:{msg}" |
| 688 | format app err (Timeout ms) = "TO:{ms}" |
| 689 | format app err _ = "UNKNOWN" |
| 690 | |
| 691 | expand type AppErr = AuthError String | DbError Int String |
| 692 | |
| 693 | fn :: format app err AppErr => String |
| 694 | format app err (AuthError user) = "AUTH:{user}" |
| 695 | format app err (DbError code db) = "DB:{code}:{db}" |
| 696 | |
| 697 | type AppConfig = host: String, port: Int |
| 698 | expand type AppConfig = tls: Bool |
| 699 | ``` |
| 700 | |
| 701 | #### Module Namespaces, Qualified Access & Strict Per-Module Import Visibility (`FR-COMP-012`) |
| 702 | |
| 703 | Every `.rg` file or `.rglib` bundle defines a module namespace (derived from its file stem or an explicit `module :: <name>` header) and may import other modules via `import <mod>` or `import <mod> as <alias>`. |
| 704 | |
| 705 | 1. **Multi-File Modules (`module :: <name>`)**: |
| 706 | - Multiple files declaring the same `module :: <name>` header (for example, `orm.rg` and `orm_changeset.rg` both starting with `module :: orm`) share a single logical module namespace `orm` and see each other's types, constructors, record fields, and functions directly. |
| 707 | 2. **Qualified Access (`mod.Type`, `mod.Constructor`, `mod.field`, `mod.fn`)**: |
| 708 | - Exported types, Sum ADT constructors, Product Record field accessors, record updates (`with`), and functions can be referenced with a `<mod_or_alias>.` prefix (`http.Config`, `db.Status`, `http.Ok 200`, `http.port cfg`, `http.check status cfg`). |
| 709 | 3. **Strict 3-Tier Per-Module Unqualified Name Resolution**: |
| 710 | - Each module `M` (including the root application module) resolves unqualified types, constructors, record fields, functions, and multi-word calls strictly against `M`'s own import graph in three priority tiers: |
| 711 | - **Tier 1 — Own Module (`M`)**: Declarations in `M` itself (across all files sharing `module :: M`) always take highest priority. |
| 712 | - **Tier 2 — Direct Imports of `M`**: If `M` does not define the name, `ridgunc` searches modules directly imported by `M` (`import D`). If a single direct import `D` defines the name, it resolves unambiguously to `D`'s declaration even if other modules in the compilation unit (transitive dependencies, sibling modules, or the root application) also define the same name. If two or more direct imports of `M` define the same name and `M` does not define it, unqualified references are rejected as ambiguous (`__ambiguous__.<name>`) and must be qualified (`d1.<name>` vs `d2.<name>`). |
| 713 | - **Tier 3 — Transitive Imports of `M`**: Consulted only when neither Tier 1 nor Tier 2 defines the name. Consequently, a direct import in `M` (`import json`) cleanly shadows a transitive helper (`codec.json get str` imported internally by `solstice`) without forcing `solstice` or `M` to rename or qualify its calls. |
| 714 | - **Isolation from Unimported Sibling Modules and the Root Application**: Declarations defined only in modules that `M` has not imported (neither directly nor transitively) — including sibling libraries imported by the root application or declarations in the root application itself when compiling an imported library `M` — are invisible to unqualified lookup inside `M` (`__unimported__.<name>`). |
| 715 | |
| 716 | --- |
| 717 | |
| 718 | ## Unified `=` Grammar (`(none)`, `|`, `||`) & Local Variables (`FR-COMP-003`) |
| 719 | |
| 720 | Every definition or binding in Ridgun — whether a function clause (`fn ::`), an operator clause (`op ::`), an immutable local variable (`x =`), a mutable local variable (`mut x =`), or a guard branch (`| cond =` / `| otherwise =`) — ends with `=` and accepts the **exact same four block/expression forms** on the right-hand side: |
| 721 | |
| 722 | | Prefix after `=` | Execution Semantics | Meaning | |
| 723 | |---|---|---| |
| 724 | | **Inline `<expr>`** | Single expression (with optional `\|\| expr -> var` lines below) | Evaluates `<expr>` directly (resolving any parallel `\|\|` DAG bindings beneath it). | |
| 725 | | **`(none)`** *(indented lines)* | **Sequential** (top-to-bottom) | Evaluates statements in order (`mut`, assignments, side effects); the last expression is the result. | |
| 726 | | **`\| <cond> = ...`** | **Conditional** (guards & nested decision trees) | Evaluates boolean conditions top-to-bottom until the first `True` branch (or `\| otherwise =`). | |
| 727 | | **`\|\| <expr>`** | **Parallel** (green-thread DAG / tuple) | Evaluates `\|\|` branches concurrently (`\|\| expr -> var` for named DAG nodes, `\|\| expr` for parallel tuple results). | |
| 728 | |
| 729 | ### 1. Guards (`|`) Always Follow `=` (Functions, Operators, Local Variables, and Nested Decision Trees) |
| 730 | |
| 731 | Guard blocks (`| cond = ...` and `| otherwise = ...`) **always appear behind an `=`**, unifying function clauses, operator clauses, local variable definitions, and nested conditional trees: |
| 732 | |
| 733 | #### Function & Operator Clauses with Guards |
| 734 | ```ridgun |
| 735 | fn :: abs Int => Int |
| 736 | abs n = |
| 737 | | n < 0 = 0 - n |
| 738 | | otherwise = n |
| 739 | |
| 740 | op :: clamp Int between Int and Int => Int |
| 741 | clamp x between lo and hi = |
| 742 | | x < lo = lo |
| 743 | | x > hi = hi |
| 744 | | otherwise = x |
| 745 | ``` |
| 746 | |
| 747 | #### Local Variable Assignment with Guards |
| 748 | Because `|` is valid after any `=`, local variables can be bound conditionally without needing an `if/else` keyword or a helper function: |
| 749 | ```ridgun |
| 750 | discount = |
| 751 | | is_vip = 20 |
| 752 | | total > 100 = 10 |
| 753 | | otherwise = 0 |
| 754 | ``` |
| 755 | |
| 756 | #### Multi-Line Sequential Blocks, Deep Decision Trees, and Nested Closers (`|.` / `||.`) |
| 757 | Each guard branch ends with `=`, so its body can itself be a sequential block (`(none)`), a parallel block (`||`), or **another nested `|` guard block** — enabling arbitrary decision trees (3–4+ levels deep) inside `| cond =` and `| otherwise =`. |
| 758 | |
| 759 | - **Closing nested levels (`|.` and `||.`)**: |
| 760 | - In simple 1-level blocks or at the end of a function/tree, **no closing token is needed**. |
| 761 | - When a nested `|` (or `||`) sub-block finishes and execution **returns to an outer level** where more branches or statements follow below it, **`|.`** (for `|`) or **`||.`** (for `||`) explicitly closes that nested level. |
| 762 | - Both **inline placement at the end of the last line** (`| otherwise = val |.`, the canonical compact style) and placement on the next line (`|.`) are valid: |
| 763 | |
| 764 | ```ridgun |
| 765 | fn :: evaluate payment Account Customer Payment => String |
| 766 | evaluate payment account customer payment = |
| 767 | | is locked account = "REJECTED_ACCOUNT_LOCKED" |
| 768 | | amount payment <= 0 = "ERROR_INVALID_AMOUNT" |
| 769 | | otherwise = |
| 770 | | tier customer == "ENTERPRISE" = |
| 771 | | amount payment > 50000 = |
| 772 | | has dual signature payment = "APPROVED_ENTERPRISE_DUAL_SIG" |
| 773 | | tenure customer > 10 = "RISK_COMMITTEE_REVIEW" |
| 774 | | otherwise = "REJECTED_ENTERPRISE_LIMIT" |. |
| 775 | | country payment != "US" = |
| 776 | | is whitelisted (country payment) = "APPROVED_ENTERPRISE_INTL" |
| 777 | | otherwise = "HELD_COMPLIANCE_INTL" |. |
| 778 | | otherwise = "APPROVED_ENTERPRISE_STANDARD" |. |
| 779 | | tier customer == "VIP" = |
| 780 | | amount payment > 10000 = |
| 781 | | has 2fa payment = "APPROVED_VIP_2FA" |
| 782 | | otherwise = "REQUEST_VIP_SMS" |. |
| 783 | | otherwise = "APPROVED_VIP_DIRECT" |. |
| 784 | | otherwise = |
| 785 | | amount payment > 1000 = |
| 786 | | has 2fa payment and risk payment < 30 = "APPROVED_RETAIL_2FA" |
| 787 | | otherwise = "REJECTED_RETAIL_RISK" |. |
| 788 | | otherwise = "APPROVED_RETAIL_LOW_AMOUNT" |
| 789 | |
| 790 | fn :: charge order Payment => Int |
| 791 | charge order payment = |
| 792 | || fetch base fee payment -> base_fee |
| 793 | || compute risk score payment -> risk_score ||. |
| 794 | surcharge = |
| 795 | | risk_score > 80 = 50 |
| 796 | | otherwise = 0 |. |
| 797 | base_fee + surcharge |
| 798 | ``` |
| 799 | |
| 800 | ### 2. Immutable by Default (`x = ...`) vs Explicit Mutable Variables (`mut x = ...`) |
| 801 | |
| 802 | Inside sequential blocks, local variables follow **Option 1**: |
| 803 | - **Immutable binding (`x = <rhs>`)**: |
| 804 | - Declares a new immutable local variable `x`. |
| 805 | - Attempting to reassign `x = ...` later in the same scope without a prior `mut x = ...` declaration is a **compile-time error**. |
| 806 | - **Mutable declaration (`mut x = <rhs>`) & Reassignment (`x = <new_rhs>`)**: |
| 807 | - `mut x = <rhs>` declares `x` as mutable in the current scope and initializes it. |
| 808 | - Subsequent reassignments to `x` in the same scope are written simply as `x = <new_rhs>` (without repeating `mut`). |
| 809 | - Under `RMM2`, reassigning `x = <new_rhs>` increments the new structure (`+1`) **before** decrementing the old structure (`-1`, cascading to its children if its counter reaches `0`). |
| 810 | |
| 811 | ```ridgun |
| 812 | fn :: accumulate Int => Int |
| 813 | accumulate n = |
| 814 | factor = 2 |
| 815 | mut acc = n * factor |
| 816 | acc = acc + 10 |
| 817 | acc = |
| 818 | | acc > 100 = 100 |
| 819 | | otherwise = acc |
| 820 | acc |
| 821 | ``` |
| 822 | |
| 823 | ### 3. Pattern Matching on Literals & ADT Constructors |
| 824 | |
| 825 | ```ridgun |
| 826 | fn :: factorial Int => Int |
| 827 | factorial 0 = 1 |
| 828 | factorial n = n * factorial (n - 1) |
| 829 | |
| 830 | fn :: unwrap Result => Int |
| 831 | unwrap (Ok n) = n |
| 832 | unwrap (Err msg) = 0 |
| 833 | |
| 834 | fn :: length List => Int |
| 835 | length Empty = 0 |
| 836 | length (Cons _ rest) = 1 + length rest |
| 837 | |
| 838 | fn :: sum tree Tree => Int |
| 839 | sum tree (Leaf n) = n |
| 840 | sum tree (Node left right) = sum tree left + sum tree right |
| 841 | ``` |
| 842 | |
| 843 | Constructors, with or without arguments, also match inside tuple and cons patterns (`FR-COMP-017`). Each tuple element is a constructor applied to the sub-patterns up to the next `,`, `:` or `)`: |
| 844 | |
| 845 | ```ridgun |
| 846 | type Step = Ask Int | End Bool | Stop |
| 847 | |
| 848 | fn :: picked (Step, Int) => Int |
| 849 | picked (End _, _) = 1 |
| 850 | picked (Ask n, _) = n |
| 851 | picked (Stop, k) = k |
| 852 | ``` |
| 853 | |
| 854 | When a call matches no clause (every pattern fails, or no guard of the last clause holds and there is no `otherwise`), the program writes `runtime error: no clause of function '<name>' matches its arguments` to standard error and exits with status 1. It never returns a zero value. |
| 855 | |
| 856 | --- |
| 857 | |
| 858 | ## Parallelism Primitives (`||`) |
| 859 | |
| 860 | Ridgun unifies all parallel execution under `||` (`where` is removed): |
| 861 | |
| 862 | ### Named Parallel DAG Bindings (`|| expr -> var`) |
| 863 | |
| 864 | ```ridgun |
| 865 | fn :: compute total Int Int => Int |
| 866 | compute total a b = x + y + z |
| 867 | || heavy calc a -> x |
| 868 | || heavy calc b -> y |
| 869 | || combine x y -> z |
| 870 | ``` |
| 871 | |
| 872 | - All `|| expr -> var` lines beneath an expression form a **parallel Directed Acyclic Graph (DAG)**. |
| 873 | - The compiler analyzes variable dependencies across `||` lines (`x` and `y` have no dependencies and run in parallel in Wave 1; `z` depends on `x` and `y` and runs in Wave 2 before `x + y + z` is evaluated). |
| 874 | - Parallel tensor slice writes (`|| expr -> out[i, j]`) are also supported. |
| 875 | |
| 876 | ### Direct Parallel Pipe Blocks (`|| expr`) |
| 877 | |
| 878 | ```ridgun |
| 879 | op :: run Conf and Conf in parallel => Res, Res |
| 880 | run a and b in parallel = |
| 881 | || run process a |
| 882 | || run process b |
| 883 | ``` |
| 884 | |
| 885 | Each `||` line runs as a separate parallel green thread, and results are returned as a tuple in declaration order (or joined when placed inside a sequential block). |
| 886 | |
| 887 | ### Typed Channels |
| 888 | |
| 889 | For streaming data and producer-consumer topologies across green threads, Ridgun provides typed MPMC channels (`Channel`): |
| 890 | |
| 891 | | Built-in Function / Operator | Signature | Purpose | |
| 892 | |---|---|---| |
| 893 | | `new channel` | `() => Channel` | Create an unbounded typed channel | |
| 894 | | `new bounded channel` | `Int => Channel` | Create a bounded channel with backpressure capacity | |
| 895 | | `send through` | `T Channel => ()` | Send a value into a channel (blocks if bounded and full) | |
| 896 | | `receive from` | `Channel => T` | Receive the next value from a channel | |
| 897 | | `try receive from` | `Channel => Option` | Non-blocking poll returning `Some v` or `None` | |
| 898 | |
| 899 | ### Native Agent Model (`agent`, `agent sink`, `agent ::` — `FR-COMP-009`) |
| 900 | |
| 901 | Ridgun provides typed actor-style agents (`Agent In Out` and fire-and-forget `SinkAgent In State`) that process messages sequentially in isolated state while running concurrently on background worker threads: |
| 902 | |
| 903 | | Construct / Built-in | Signature / Syntax | Purpose | |
| 904 | |---|---|---| |
| 905 | | `agent <fn>` | `(In => Out) => Agent In Out` | Spawn a stateless FIFO agent | |
| 906 | | `agent <fn> <init_state>` | `(S In => S Out) S => Agent In Out` | Spawn a stateful agent whose step returns `(new_state, reply)` | |
| 907 | | `agent sink <fn> <init_state> [bounded N drop\|block] [drain D]` | `(S In => S) S => SinkAgent In S` | Spawn a fire-and-forget sink agent with no output queue | |
| 908 | | `agent :: <name> = agent sink ...` | Top-level declaration | Declare a named global sink agent accessible across threads | |
| 909 | | `send <msg> to <ag>` | `In (Agent In Out) => ()` | Enqueue a message asynchronously | |
| 910 | | `ask <msg> to <ag>` | `In (Agent In Out) => Out` | Send a message and wait for this caller's reply | |
| 911 | | `receive from <ag>` | `(Agent In Out) => Out` | Wait for and return the next reply for the calling thread | |
| 912 | | `try receive from <ag>` | `(Agent In Out) => Option Out` | Non-blocking poll returning `Some v` or `None` | |
| 913 | | `stop <ag>` / `agent alive <ag>` | `Agent => ()` / `Agent => Bool` | Gracefully stop an agent or check if it is still running | |
| 914 | | `agent flush <ag>` / `agent state <ag>` / `agent dropped <ag>` | Inspection & testing | Drain pending messages, read current state, or read dropped count | |
| 915 | |
| 916 | --- |
| 917 | |
| 918 | ## I/O Primitives |
| 919 | |
| 920 | Ridgun provides built-in functions for terminal output. |
| 921 | |
| 922 | ### `echo` |
| 923 | |
| 924 | Prints an arbitrary number of strings to the terminal, separated by spaces, followed by a newline. |
| 925 | |
| 926 | ``` |
| 927 | fn :: echo String... => () |
| 928 | ``` |
| 929 | |
| 930 | The `...` suffix on the last parameter type marks it as **variadic** — accepting one or more arguments of that type. |
| 931 | |
| 932 | ```ridgun |
| 933 | echo "hello" -- prints: hello |
| 934 | echo "hello" "world" "!" -- prints: hello world ! |
| 935 | echo "result: {result}" -- prints: result: 42 |
| 936 | ``` |
| 937 | |
| 938 | ### `out` |
| 939 | |
| 940 | An operator that writes a string directly to the terminal and returns it (allowing chaining). |
| 941 | |
| 942 | ``` |
| 943 | op :: String out => String |
| 944 | ``` |
| 945 | |
| 946 | ```ridgun |
| 947 | "hello" out -- prints: hello |
| 948 | "step 1 done" out -- prints: step 1 done |
| 949 | |
| 950 | -- Chaining: out returns the string, so it can be passed onward |
| 951 | fn :: log and process String => String |
| 952 | log and process s = process (s out) and report |
| 953 | ``` |
| 954 | |
| 955 | ### Summary |
| 956 | |
| 957 | | Function | Type | Purpose | |
| 958 | |---|---|---| |
| 959 | | `echo` | `String... => ()` | Print strings with spaces and newline | |
| 960 | | `out` | `String => String` | Print string, return it for chaining | |
| 961 | |
| 962 | --- |
| 963 | |
| 964 | ## Standard Library |
| 965 | |
| 966 | Libraries are imported with `import`: |
| 967 | |
| 968 | ```ridgun |
| 969 | import test |
| 970 | ``` |
| 971 | |
| 972 | ### Unit Testing (`lib/test.rg`) |
| 973 | |
| 974 | A simple test library using `echo` for terminal output. |
| 975 | |
| 976 | **Assertions:** |
| 977 | |
| 978 | | Operator | Purpose | |
| 979 | |---|---| |
| 980 | | `assert Int equals Int` | Integer equality | |
| 981 | | `assert Real equals Real` | Real equality | |
| 982 | | `assert String equals String` | String equality | |
| 983 | | `assert Bool is true` | Boolean is true | |
| 984 | | `assert Bool is false` | Boolean is false | |
| 985 | |
| 986 | **Test structure:** |
| 987 | |
| 988 | ```ridgun |
| 989 | import test |
| 990 | |
| 991 | fn :: main => () |
| 992 | main = |
| 993 | describe "Arithmetic" does |
| 994 | |
| 995 | test "sum adds correctly" does |
| 996 | assert (sum 3 4) equals 7 |
| 997 | |
| 998 | test "factorial base case" does |
| 999 | assert (factorial 0) equals 1 |
| 1000 | ``` |
| 1001 | |
| 1002 | **Terminal output:** |
| 1003 | |
| 1004 | ``` |
| 1005 | ═══ Arithmetic ═══ |
| 1006 | ● sum adds correctly |
| 1007 | ✓ PASS |
| 1008 | ● factorial base case |
| 1009 | ✓ PASS |
| 1010 | ``` |
| 1011 | |
| 1012 | --- |
| 1013 | |
| 1014 | ## Summary of Syntactic Elements |
| 1015 | |
| 1016 | | Element | Syntax | Purpose | |
| 1017 | |---|---|---| |
| 1018 | | Comment | `-- text` | Single-line comment | |
| 1019 | | Function declaration | `fn :: name types => return` | Declare a function's type signature | |
| 1020 | | Function definition | `name params = body` | Define a function's implementation | |
| 1021 | | Operator declaration | `op :: words types words => return` | Declare a natural language operator | |
| 1022 | | Operator definition | `words params words = body` | Define an operator's implementation | |
| 1023 | | Multiple returns | `=> type, type` | Declare multiple return values | |
| 1024 | | Variadic parameters | `Type...` | Accept one or more arguments of a type | |
| 1025 | | Tensor literal | `Tensor [[1, 2], [3, 4]]` | Create an N-dimensional tensor | |
| 1026 | | Tensor indexing | `t[0, 1]` or `t[0][1]` | Access tensor elements | |
| 1027 | | Tensor mutation | `t[0, 1] = 5.0` | Mutate tensor elements in place | |
| 1028 | | Einstein contraction | `A_ij @ B_jk` | Tensor contraction over shared indices | |
| 1029 | | Hadamard operation | `A_ij * B_ij` | Element-wise arithmetic with index alignment | |
| 1030 | | `echo` | `echo "a" "b" "c"` | Print strings to terminal | |
| 1031 | | `out` | `"text" out` | Output string to terminal | |
| 1032 | | Immutable local binding | `x = expr` | Bind an immutable local variable (`=` supports `(none)`, `\|`, `\|\|`) | |
| 1033 | | Mutable local binding | `mut x = expr` | Declare a mutable local variable (reassigned via `x = new_expr`) | |
| 1034 | | Guard block | `\| cond = expr` | Conditional guard branch after `=` (supports nested `\|`) | |
| 1035 | | Parallel DAG block | `\|\| expr -> var` | Parallel sub-computations with named DAG results | |
| 1036 | | Parallel pipe block | `\|\| expr` | Parallel execution returning a tuple | |
| 1037 | | Operator overloading | `op :: type + type => type` | Overload existing operators for new types | |
| 1038 | | Bool literals | `True`, `False` | Boolean primitive values | |
| 1039 | | Logical operators | `and`, `or`, `xor`, `not` | Word-based lowercase logical operators | |
| 1040 | | Entry point | `fn :: main => ()` | Required program entry point | |
| 1041 | |
| 1042 | --- |
| 1043 | |
| 1044 | ## Canonical Source Code Formatting (`stable format` & Editor Tooling) |
| 1045 | |
| 1046 | > **Aesthetic Only — Not a Compiler Requirement (`FR-TOOL-001`)**: Ridgun's lexer and parser are insensitive to horizontal column alignment. Unaligned and column-aligned `.rg` source files are 100% equivalent to `ridgunc`. The rules below define the **canonical aesthetic layout** applied by `stable format` and GNU Emacs `ridgun-mode` (`C-c C-f`). |
| 1047 | |
| 1048 | 1. **Alignment Group per Signature (`fn ::` / `op ::`)**: |
| 1049 | - Each `fn ::` or `op ::` declaration starts an alignment group spanning its signature, its pattern-matching clauses, and its `|| expr -> var` bindings, ending at the next top-level declaration (`fn`, `op`, `type`, `import`, `global`, `context`, `shared`, `hardware`, `@`). |
| 1050 | 2. **Column Alignment of LHS Tokens ($C_0 = 6$)**: |
| 1051 | - `fn :: ` and `op :: ` are 6 characters wide, so token $0$ of the signature LHS starts at column $C_0 = 6$. |
| 1052 | - Each token slot $i \in [0, N-1]$ before `=>`/`->` (treating `"..."`, `'...'`, `[...]`, and `(...)` as single tokens) has width $W_i = \max(\text{len}(\text{sig\_tok}_i), \max_{\text{clause} : \text{len}(\text{clause\_tok}_i) \le 18} \text{len}(\text{clause\_tok}_i))$ and starts at column $C_i = C_{i-1} + W_{i-1} + 1$. Clause patterns wider than `18` characters are treated as outliers: they do not stretch $W_i$ for the rest of the group and simply overflow on their own line with 1 space. |
| 1053 | - Matching definition clauses are indented to column $6$ ($C_0$) so the function name or operator words and every argument pattern align vertically beneath their corresponding signature slots. |
| 1054 | 3. **Vertical Alignment of `=>`, `=`, and `->` ($C_{\text{arrow}}$)**: |
| 1055 | - Let $C_{\text{arrow}} = \max\big(C_{N-1} + W_{N-1} + 1,\; \max_{\text{pipe\_bind}}(8 + 3 + \text{len}(\text{pipe\_lhs}) + 1)\big)$ and $C_{\text{ret}} = C_{\text{arrow}} + \text{len}(\text{arrow}) + 1$. |
| 1056 | - In the signature, `=>` is placed at $C_{\text{arrow}}$ and the return type at $C_{\text{ret}}$. |
| 1057 | - In single-line clauses (`<lhs> = <rhs>`), multi-line block clauses (`<lhs> =`), and guard-headed clauses (`<lhs> =` followed by `| cond = ...`), `=` is placed at $C_{\text{arrow}}$ (aligned directly beneath `=>`); in single-line clauses, `<rhs>` starts at $C_{\text{ret}}$. |
| 1058 | - In parallel DAG bindings (`|| <lhs> -> <rhs>`), `||` starts at column $8$ (`6 + 2`), `->` is placed at $C_{\text{arrow}}$, and `<rhs>` starts at $C_{\text{ret}}$. |
| 1059 | - **Compact Fallback (`> 4` Parameters or `> 80` Columns)**: If a declaration has more than `4` parameter slots or its aligned signature line would exceed `80` characters, horizontal column padding is disabled for that group and tokens are separated by a single space (`" "`) while preserving column-6 clause indentation. |
| 1060 | 4. **Structural Indentation (2-Space Offset)**: |
| 1061 | - Top-level declarations (`fn`, `op`, `type`, `import`, `global`, `context`, `shared`, `hardware`, `@`): column `0`. |
| 1062 | - Function/operator clauses under a signature: column `6`. |
| 1063 | - Guards (`| cond = expr`) and parallel pipes (`|| expr` / `|| expr -> var`): `prev_indent + 2` (column `8` inside a clause, or `indent + 2` inside a local binding or nested guard). |
| 1064 | - Sequential block body lines following a line ending in `=`: `prev_indent + 2`. |
| 1065 | |
| 1066 | --- |
| 1067 | |
| 1068 | ## Sequential & Parallel Higher-Order Collection Operations (`FR-COMP-001`, `FR-COMP-014`) |
| 1069 | |
| 1070 | Ridgun provides standard sequential collection combinators (`map`, `filter`, `fold`) alongside explicit multicore parallel combinators prefixed with `||` (`||map`, `||filter`). All five combinators support both **idiomatic left-to-right pipeline/infix syntax** (`xs map f -> filter g -> fold 0 h -> res`) and traditional prefix syntax (`map f xs`): |
| 1071 | |
| 1072 | | Built-in | Prefix & Pipeline Forms | Execution Model | |
| 1073 | |---|---|---| |
| 1074 | | `map` | `xs map f` / `xs -> map f` / `map f xs` | **Sequential**: evaluates `f x` strictly left-to-right on the calling thread. | |
| 1075 | | `||map` | `xs \|\|map f` / `xs -> \|\|map f` / `\|\|map f xs` | **Parallel (`\|\|`)**: evaluates `f x` across list elements concurrently on worker threads, preserving output order. | |
| 1076 | | `filter` | `xs filter p` / `xs -> filter p` / `filter p xs` | **Sequential**: evaluates predicate `p x` strictly left-to-right on the calling thread. | |
| 1077 | | `||filter` | `xs \|\|filter p` / `xs -> \|\|filter p` / `\|\|filter p xs` | **Parallel (`\|\|`)**: evaluates predicate `p x` concurrently on worker threads and retains matching elements in order. | |
| 1078 | | `fold` | `xs fold init f` / `xs -> fold init f` / `fold f init xs` | **Sequential left fold**: accumulates `acc = f acc x` from left to right starting from `init`. | |
| 1079 | |
| 1080 | --- |
| 1081 | |
| 1082 | ## Verbatim File I/O, Byte Streams & POSIX Built-ins (`FR-VM-001`, `FR-VM-002`) |
| 1083 | |
| 1084 | 1. **Verbatim File & Byte Stream I/O (`FR-VM-001`)**: |
| 1085 | - `read file : String => String` reads file contents verbatim (preserving `\r\n` and exact EOF newline presence/absence, and never running the compiler's `.rg` `import` preprocessor on data files). |
| 1086 | - `load rg source : String => String` is used exclusively by the compiler toolchain when loading `.rg` source files with `import` splicing. |
| 1087 | - `read bytes : String => [Int]` and `write bytes : String [Int] => ()` read and write raw binary byte streams (`0..255`, including `0x00` NUL and `0x80..0xFF`). |
| 1088 | 2. **POSIX Filesystem, Process & Security Built-ins (`FR-VM-002`)**: |
| 1089 | - **Filesystem**: `list dir`, `file lstat`, `sha256 file`, `sys mkdir`, `sys chmod`. |
| 1090 | - **Process & Environment**: `sys exec`, `sys chdir`, `sys getcwd`, `sys getenv`, `sys setenv`, `sys exit`. |
| 1091 | - **Security & Privilege Separation**: `sys getuid`, `sys getgid`, `sys username`, `sys user groups`, `sys lookup uid gid`, `sys setresgid`, `sys setgroups`, `sys setresuid`, `sys execve`, `sys read pass`. |
| 1092 | 3. **Cross-Platform PAL I/O, Polling, Process Primitives, Target ADTs & Declarative `with timeout` Quotas (`FR-COMP-007`)**: |
| 1093 | - **Pre-Declared Target Platform ADTs**: |
| 1094 | - `type SysOs = Linux | MacOS | Windows` (`sys os => SysOs`, open to extension via `expand type SysOs = ...`) |
| 1095 | - `type SysArch = X86_64 | Arm64` (`sys arch => SysArch`, open to extension via `expand type SysArch = ...`) |
| 1096 | - **Duration Unit Literals**: Integer literals accept `_ms` (`* 1`), `_s` (`* 1000`), and `_min` (`* 60000`) suffixes (e.g., `500_ms`, `5_s`, `2_min`), complementing byte-unit suffixes (`_KB`, `_MB`, `_GB`). |
| 1097 | - **Layer 1 — Cross-Platform PAL Primitives for `.rglib` System Libraries (`posix.rglib` / `io.rglib`)**: |
| 1098 | - `sys os => SysOs` and `sys arch => SysArch` (compile-time target OS and architecture Sum ADTs) |
| 1099 | - `sys open mode String String => Int` (`path`, portable mode `"r"`, `"w"`, `"a"`, `"rw"`, `"w+"`, `"a+"` -> descriptor/handle `>= 0` or `-errno`, translated per OS target) |
| 1100 | - `sys open String Int Int => Int` (`path`, Ridgun PAL normalized `flags` (`0` = read, `1` = write, `2` = rdwr, `64` = create, `512` = trunc, `1024` = append, `2048` = nonblock), `mode` -> `fd >= 0` or `-errno`) |
| 1101 | - `sys close Int => Int` (`fd` -> `0` or `-errno`) |
| 1102 | - `sys read fd Int Int => String` (`fd`, `max_bytes` -> string read from `fd`) |
| 1103 | - `sys write fd Int String => Int` (`fd`, `data` -> bytes written or `-errno`) |
| 1104 | - `sys poll Int Int Int => Int` (`fd`, Ridgun PAL normalized `events` (`1` = `POLLIN`/read, `4` = `POLLOUT`/write), `timeout_ms` -> `> 0` revents when ready, `0` on timeout, `< 0` on `-errno`; uses `sys remaining ms` when `timeout_ms < 0` inside `with timeout`) |
| 1105 | - `sys terminate Int Bool => Int` (`pid`, `force` -> portable graceful/forced process termination) and `sys kill Int Int => Int` (`pid`, normalized signal -> `0` or `-errno`) |
| 1106 | - `sys call Int Int... => Int` (`nr` plus `0..6` arguments `a1..a6` -> low-level target-OS ABI syscall escape hatch) |
| 1107 | - `sys time ms => Int` and `sys sleep ms Int => ()` (monotonic millisecond clock and sleep) |
| 1108 | - `sys deadline ms => Int` and `sys remaining ms => Int` (active lexical timeout deadline and remaining milliseconds, or `-1` when unbounded) |
| 1109 | - **Layer 2 — Declarative `with timeout <duration> do` Execution Quota**: |
| 1110 | - Just as `with region 64_KB do` sets a lexical byte quota, `with timeout 500_ms do` (or combined `with region 64_KB, timeout 500_ms do`) sets a lexical execution deadline (`sys time ms + duration_ms`) on the current thread context and restores the previous deadline upon block exit: |
| 1111 | ```ridgun |
| 1112 | with timeout 500_ms do |
| 1113 | rem = sys remaining ms |
| 1114 | rc = sys poll fd 1 rem |
| 1115 | rc |
| 1116 | ``` |
| 1117 | - See [`docs/lang/pal-and-system-libraries.md`](pal-and-system-libraries.md) for the complete PAL & System Libraries (`.rglib`) architecture specification. |
| 1118 | 4. **Reusable Library Compilation (`.rglib` & ELF64 `.so`) and Extensible `stable` (`FR-COMP-006`, `FR-TOOL-003`)**: |
| 1119 | - `ridgunc build --lib <file.rg> -o <out.rglib>` and `ridgunc build --so <file.rg> -o <lib.so>` (or `stable built lib` / `stable new --lib <name>`) compile modules without requiring `fn :: main => ()` and preserve all exported functions, operators, and types. |
| 1120 | - `stable` discovers custom `fn :: task <words...> => String` commands and `fn :: plugins => [String]` extensions (`plugins/*.rg`, `.stable/plugins/*.rg`), listed via `stable tasks` and `stable plugins`. |
| 1121 | |
| 1122 | --- |
| 1123 | |
| 1124 | ## Ridgun Memory Management (RMM): `RMM1` (CMM) & `RMM2` (`FR-COMP-002`) |
| 1125 | |
| 1126 | Ridgun manages heap memory (`String`, `[a]`, `(a, b)`, ADTs, `Tensor`) deterministically without a tracing Garbage Collector (GC) through **Ridgun Memory Management (RMM)**: |
| 1127 | |
| 1128 | 1. **Policy `RMM1` — Compiled Memory Management (CMM Linear + Conditional)**: |
| 1129 | - Applied when control flow and called functions are statically known at compile time over non-nested bindings: |
| 1130 | - **Linear CMM (`rg_cmm_move` / `rg_cmm_drop`)**: The compiler determines the exact last-use statement of every local variable at compile time, moving returned variables (`rg_cmm_move(&mut lv_x)`) and emitting unconditional deallocation (`rg_cmm_drop(&mut lv_x)`) immediately after a variable's last use when no may-aliases remain live. |
| 1131 | - **Conditional CMM (`rg_cmm_drop_unless` / `ptr(a) != ptr(c)`)**: When a static branching call (`c = choose list cond a b`) returns one of several candidate inputs and one or more candidates remain live alongside `c`, the compiler builds the **Ancestor May-Alias Table (`RCmmAlias`)** and emits a 1-cycle pointer-identity guard at each variable's static last-use statement: |
| 1132 | ```ridgun |
| 1133 | a = make a () -- Allocates M1 |
| 1134 | b = make b () -- Allocates M2 |
| 1135 | c = choose list cond a b -- b dies here while c is live -> if ptr(b) != ptr(c) { free(b) } |
| 1136 | print list a -- a dies here while c is live -> if ptr(a) != ptr(c) { free(a) } |
| 1137 | consume list c -- c dies here (no live peers) -> free(c) (unconditional) |
| 1138 | ``` |
| 1139 | 2. **Policy `RMM2` — Assignment & Cascaded Structure Counting (Active Policy)**: |
| 1140 | - Unifies memory management across functions, branching returns (`choose list`), nested containers (`xs = [a, b, ..., z]`, `xs[3] = a`), mutable local variables (`mut x = ...`), and multi-thread execution: |
| 1141 | - **Primitive Scalars (`Int`, `Real`, `Bool`, `()`)**: Never counted; copied by value on the stack/registers. |
| 1142 | - **Incoming Function Arguments (`pats`)**: **Never touched** (`+0` on entry, `-0` on exit). |
| 1143 | - **Internal Local Variables (`x = ...` / `mut x = ...`)**: Receive **`+1` on assignment** (`x = expr`, `xs[3] = a`). On mutable reassignment (`x = new_expr`), `new_expr` receives `+1` before old `x` receives `-1`. |
| 1144 | - **End of Function / Scope Exit**: |
| 1145 | - Non-returned internal local variables receive `-1` first: if `rc == 0`, the parent block is freed and **cascades `-1` one by one to each direct child structure** (recursively freeing any child whose `rc` reaches `0`). |
| 1146 | - A returned internal local variable (`return a`) receives `-1` (or is moved via `rg_cmm_move`), and even if its `rc` is `0` in transit, it is **spared** so the caller's assignment (`c = make pair ()`) adds `+1` (`0 + 1 = 1`). When an incoming argument is returned (`choose list cond a b`), it was untouched inside the function (`rc = 1`), so the caller's `c = choose list cond a b` adds `+1` (`1 + 1 = 2`, shared between `a` and `c`). |
| 1147 | 3. **Policy `RMM3`** *(Reserved)*: |
| 1148 | - Reserved for a subsequent aggressive static optimization policy. |
| 1149 | - See [`docs/etc/cmm_architecture.md`](../etc/cmm_architecture.md) for the complete `RMM1` and `RMM2` specification. |
| 1150 | |
| 1151 |