Skip to content
Ridgun
English

Ridgun

The language itself: a compiler that compiles itself, the stable project tool, the specification and the book

spec.md1151 lines · 59.5 KBRaw
1# Ridgun Language Specification
2
3## Overview
4
5Ridgun 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
7Key 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
17Single-line comments use `--`:
18
19```ridgun
20-- This is a comment
21```
22
23---
24
25## Strings
26
27Ridgun has two string literal types with different interpolation behavior.
28
29### Interpolated Strings (double quotes)
30
31Double-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
34name = "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
40To 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
50Single-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
69All 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
72fn :: main => ()
73 main = echo "Hello, Ridgun!"
74```
75
76A 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
82No loose top-level expressions are allowed. Everything that runs must be called from `main`.
83
84### CLI Usage
85
86```
87ridgunc <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
101Common 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
107Functions 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
133To 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
141The 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
149Operators 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```
154op :: <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
162op :: add Int to Int => Int
163 add a to b = a + b
164```
165
166Usage would read as: `add 3 to 5` → `8`
167
168The 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
172Operators 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)
176op :: 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
182Key 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
190When 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
194op :: 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
200Each `||` line runs as an independent parallel task. Results are collected in declaration order.
201
202### Operator Overloading
203
204Existing operators (including built-in ones like `+`) can be overloaded for new types:
205
206```ridgun
207-- Overload + for Complex numbers
208op :: 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
216When 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
228Operators 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
232Ridgun 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):
236xs map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results
237
238-- Equivalent unified forms (all normalize to the exact same AST):
239xs -> map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results -- (2) explicit -> from subject
240xs map (x => x * 2) filter (x => x > 6) compact limit 14 -> results -- (1) contiguous infix chain
241map (x => x * 2) xs -> filter (x => x > 6) -> compact limit 14 -> results -- (3) prefix start + -> chain
242```
243
244Multi-stage pipelines can also be written across indented continuation lines starting with `->`:
245
246```ridgun
247raw_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
265When 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
268parse header packet "v0.9.9" -> (+ _ _ +) -> combine code msg -> status_line
269```
270
271#### 4. `_ =`-Free Side-Effect Statements & Explicit Unit Discard (`-> ()`)
272
273In 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
276verify strict if enabled strict_mode input_path
277s_errs -> format type errors -> print error lines
278echo "[ridgunc --strict] {input_path}: {err_c} error(s) found."
279sys exit 1
280```
281
282When 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
285fn :: 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
292Operators that return sequences can also be chained point-free:
293
294```ridgun
295op :: Int Int switch -> Int Int
296 a b switch = b a
297
298op :: 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
310Type signatures appear in declarations after `fn ::` or `op ::` (or without a prefix):
311
312```
313fn :: <name> <type1> <type2> ... => <return_type1> <return_type2> ...
314op :: <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
368True -- boolean true
369False -- 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
375fn :: is valid Int => Bool
376 is valid n =
377 | n > 0 = True
378 | otherwise = False
379```
380
381### Logical Operators
382
383Ridgun 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
393result = True and False -- False
394result = True or False -- True
395result = True xor True -- False
396result = not False -- True
397```
398
399These are infix keyword operators with standard boolean semantics.
400
401### Native Collection Types
402
403#### Lists
404
405Lists 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
410fn :: sum list [Int] => Int
411fn :: reverse [Int] => [Int]
412```
413
414**Literal syntax:** `[elem1, elem2, ...]` — a list literal. `[]` is the empty list.
415
416```ridgun
417fn :: my list => [Int]
418my 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
424fn :: sum list [Int] => Int
425 sum list [] = 0
426 sum list (x:xs) = x + sum list xs
427
428fn :: 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
436fn :: append [Int] [Int] => [Int]
437 append a b = a :: b
438```
439
440#### Tensors
441
442Tensors 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
447v = Tensor [1, 2, 3] -- 1D (vector), shape: (3)
448m = Tensor [[1, 2], [5, 3]] -- 2D (matrix), shape: (2, 2)
449t = 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
456val = m[0][1] -- chained indexing: row 0, col 1
457val = m[0, 1] -- comma indexing: equivalent
458row = 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
464m[0, 1] = 5.0 -- set element
465m[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)
474T = A_sdf @ B_ndf
475
476-- Equivalent to: np.einsum('sdf,ndf->sn', A, B)
477```
478
479Contraction 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)
484R = 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
493H = A_ij * B_ij -- shape (i, j)
494
495-- Broadcasting: B has fewer dimensions
496S = A_ij * B_j -- B broadcasts along i
497
498-- Element-wise add
499C = A_ij + B_ij
500
501-- Element-wise divide
502D = A_ij / B_j -- B broadcasts along i
503```
504
505This 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
510g = Tensor_GPU [[1, 2], [3, 4]]
511result = g_ij @ g_jk -- GPU-accelerated matrix multiply
512```
513
514#### Sequences (`seq`)
515
516Sequences 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:
520fn :: sum Int Int => Int -- params: seq(Int, Int)
521fn :: 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
527fn :: first of seq Int Int => Int
528 first of seq (s) = s(+ _)
529
530-- Equivalent without seq binding:
531fn :: 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
538s(+ _) -- select 1st, skip 2nd
539s(_ +) -- skip 1st, select 2nd
540s(+ _ _ _) -- select 1st, skip 2nd-4th
541s(_ _ _ +) -- skip 1st-3rd, select 4th
542```
543
544**Index selector (`s(0 3)`)** — extracts elements by their zero-based positions:
545
546```ridgun
547fn :: 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
554seq1 = a, b -- seq(a, b)
555seq2 = c, d -- seq(c, d)
556seq1:seq2 -- seq(a, b, c, d)
557```
558
559**Nesting (`,`)** — composes sequences as distinct elements (seq of seqs):
560
561```ridgun
562seq1, seq2 -- seq(seq(a, b), seq(c, d))
563```
564
565### Algebraic Data Types (ADTs)
566
567Ridgun 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
571A type with named fields:
572
573```ridgun
574type Conf = host: String, port: Int, tls: Bool
575type Point = x: Real, y: Real
576```
577
578Fields are accessed by name as functions (`host c`, `port c`), and updated functionally using `with` and `=`:
579
580```ridgun
581fn :: 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):
586fn :: enable tls Conf => Conf
587 enable tls c = c with tls = True, port = 443
588```
589
590#### Sum Types (variants)
591
592A type with multiple constructors, separated by `|`:
593
594```ridgun
595type Result = Ok Int | Err String
596type Option = Some Int | None
597type Color = Red | Green | Blue
598```
599
600#### Generic Sum ADTs & Generic Product Records (`FR-COMP-013`)
601
602User-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
605type Result T E = Ok T | Err E
606type Option A = Some A | None
607type Box A = Box A
608type Page T = items: [T], total: Int
609type 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
618Anonymous 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
621fn :: add offset Int [Int] => [Int]
622 add offset k xs = map (x => x + k) xs
623```
624
625Five canonical rules govern anonymous functions:
626
6271. **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)'`).
6282. **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.
6293. **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.
6304. **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 ```
6375. **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
639Captured bindings are lifted automatically into partial-application closures managed by `RMM1`/`RMM2`.
640
641#### Recursive Types
642
643Types can reference themselves:
644
645```ridgun
646type List = Empty | Cons Int List
647type Tree = Leaf Int | Node Tree Tree
648```
649
650#### Shared Constructor Names Across Types (`FR-COMP-005`)
651
652Multiple `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
655type ParseOut = Parsed Int | Error String
656type DbOut = Rows Int | Error Int String
657type MiniExpr = Var String | Lit Int
658type MiniPat = Var String | Lit Int | Wild
659```
660
661#### Open Type Extensions (`expand type`) & Automatic `fn ::` Clause Merging (`FR-COMP-005`)
662
663Existing 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
671type Something = String | Int | MyStruct Float Int
672expand type Something = Bool
673
674fn :: 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
684type AppErr = IoError String | Timeout Int
685
686fn :: 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
691expand type AppErr = AuthError String | DbError Int String
692
693fn :: format app err AppErr => String
694 format app err (AuthError user) = "AUTH:{user}"
695 format app err (DbError code db) = "DB:{code}:{db}"
696
697type AppConfig = host: String, port: Int
698expand type AppConfig = tls: Bool
699```
700
701#### Module Namespaces, Qualified Access & Strict Per-Module Import Visibility (`FR-COMP-012`)
702
703Every `.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
7051. **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.
7072. **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`).
7093. **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
720Every 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
731Guard 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
735fn :: abs Int => Int
736 abs n =
737 | n < 0 = 0 - n
738 | otherwise = n
739
740op :: 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
748Because `|` is valid after any `=`, local variables can be bound conditionally without needing an `if/else` keyword or a helper function:
749```ridgun
750discount =
751 | is_vip = 20
752 | total > 100 = 10
753 | otherwise = 0
754```
755
756#### Multi-Line Sequential Blocks, Deep Decision Trees, and Nested Closers (`|.` / `||.`)
757Each 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
765fn :: 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
790fn :: 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
802Inside 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
812fn :: 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
826fn :: factorial Int => Int
827 factorial 0 = 1
828 factorial n = n * factorial (n - 1)
829
830fn :: unwrap Result => Int
831 unwrap (Ok n) = n
832 unwrap (Err msg) = 0
833
834fn :: length List => Int
835 length Empty = 0
836 length (Cons _ rest) = 1 + length rest
837
838fn :: sum tree Tree => Int
839 sum tree (Leaf n) = n
840 sum tree (Node left right) = sum tree left + sum tree right
841```
842
843Constructors, 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
846type Step = Ask Int | End Bool | Stop
847
848fn :: picked (Step, Int) => Int
849 picked (End _, _) = 1
850 picked (Ask n, _) = n
851 picked (Stop, k) = k
852```
853
854When 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
860Ridgun unifies all parallel execution under `||` (`where` is removed):
861
862### Named Parallel DAG Bindings (`|| expr -> var`)
863
864```ridgun
865fn :: 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
879op :: 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
885Each `||` 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
889For 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
901Ridgun 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
920Ridgun provides built-in functions for terminal output.
921
922### `echo`
923
924Prints an arbitrary number of strings to the terminal, separated by spaces, followed by a newline.
925
926```
927fn :: echo String... => ()
928```
929
930The `...` suffix on the last parameter type marks it as **variadic** — accepting one or more arguments of that type.
931
932```ridgun
933echo "hello" -- prints: hello
934echo "hello" "world" "!" -- prints: hello world !
935echo "result: {result}" -- prints: result: 42
936```
937
938### `out`
939
940An operator that writes a string directly to the terminal and returns it (allowing chaining).
941
942```
943op :: 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
951fn :: 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
966Libraries are imported with `import`:
967
968```ridgun
969import test
970```
971
972### Unit Testing (`lib/test.rg`)
973
974A 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
989import test
990
991fn :: 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
10481. **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`, `@`).
10502. **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.
10543. **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.
10604. **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
1070Ridgun 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
10841. **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`).
10882. **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`.
10923. **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.
11184. **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
1126Ridgun manages heap memory (`String`, `[a]`, `(a, b)`, ADTs, `Tensor`) deterministically without a tracing Garbage Collector (GC) through **Ridgun Memory Management (RMM)**:
1127
11281. **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 ```
11392. **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`).
11473. **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