diff options
| author | tslil <tslil@posteo.de> | 2025-10-18 11:40:42 +0100 |
|---|---|---|
| committer | tslil <tslil@posteo.de> | 2025-10-18 14:02:12 +0100 |
| commit | 7cb0b2d8f1dad40e7b90ced7b19bb32aaf0eaff3 (patch) | |
| tree | 0ba057ee5114b910f73ed14102aef26f068f42a3 | |
| parent | a775b8f56e5422e47c40927328baf3045c500a32 (diff) | |
Clarify many points of ambiguity, add swap, hooks, explanations about various conventions
| -rw-r--r-- | rprt.md | 204 |
1 files changed, 166 insertions, 38 deletions
@@ -13,7 +13,7 @@ All functions work with two core types: Every selection exists within one or more buffers. Selections have a rank: - **Rank 0**: Position — a natural number offset within a single buffer - - **Rank 1**: Range — a pair of positions [start,end) with capture groups, within a single buffer + - **Rank 1**: Range — a pair of positions `[start, end)` with capture groups, within a single buffer - **Rank 2**: Ranges — a list of ranges with capture groups, within a single buffer - **Rank 3**: Multi-buffer selection — a mapping from buffer identifiers to rank 2 selections @@ -29,17 +29,78 @@ A **Text function** is something of signature `Selection × Text → Selection`. Following APL conventions, functions may have nullary, monadic, and dyadic forms. In these cases the number of arguments that they take varies (though not their types). -### [Leading axis theory](https://aplwiki.com/wiki/Leading_axis_theory) and broadcasting +### Type Coercion -The leading axis is the buffer selection, followed by the range _index_, followed by the character position in each range. When broadcasting occurs axes are aligned. +When a Text argument is required, a Selection is implicitly coerced to Text by extracting its contents. This coercion has no side effects. + +| Input rank | Coerced Text | +|------------|---------------------------------------------------| +| 0 | Empty string (position has no extent) | +| 1 | Contents of the range | +| 2 | Contents of each range (multiple text values) | +| 3 | Contents per buffer per range (multiple values) | + +For ranks 2 and 3, the selection provides multiple text values. Text functions pair these values with selection ranges following the broadcasting rules in §1.2. + +**Text constants as functions:** A Text constant (string literal) `"text"` can be used as a constant nullary or monadic Text function that always returns `"text"`, ignoring its argument. This allows Text constants to be composed with other functions using combinators like hooks and trains. + +## 1.2 Notation, [Leading axis theory](https://aplwiki.com/wiki/Leading_axis_theory), and broadcasting + +**Notation:** In dyadic function application `α F ω`, `α` (alpha) is the left argument, `F` is the function, and `ω` (omega) is the right argument. For Selection functions `ω` may be omitted to obtain the monadic form `α F` of `F`. For Text functions `α` may be omitted to obtain the monadic form `F ω` of `F`. + +Selections have a hierarchical structure with two axes, ordered from outermost (leading) to innermost: + +1. **Buffer axis**: Distinct buffer identifiers (rank 3 only) +2. **Range axis**: Range index within list of ranges (ranks 2-3) -TODO `α F ω` explain +Selection functions define distinct **rank 0** and **rank 1** semantics. Higher rank selections are processed by **vectorisation**: the rank 1 semantics are applied to each range. + +**Monadic rank polymorphism** for `α F`: + +| `α` rank | Semantics | +|----------|-----------------------------------------------------------------------------------------------| +| 0 | Apply rank 0 semantics of `F` | +| 1 | Apply rank 1 semantics of `F` | +| 2 | Vectorise: collect `α' F` into ranges, where `α'` varies over each range in `α` | +| 3 | Vectorise: collect `α' F` per buffer, where `α'` varies over the ranges in each buffer of `α` | + +**Dyadic selection functions:** The span function `-` (§2.1) is the only dyadic selection function. It broadcasts its rank 0 and rank 1 semantics across higher ranks following the table above. See §2.1 for the complete broadcasting table. + +**Text functions:** Text functions `α F τ` (§3) broadcast along the selection's axes when `α` has rank 2+. If the text argument `τ` is itself a Selection that coerces (§1.1) to multiple text values (rank 2+), the text values are paired with the ranges in `α`. + +The buffer axis (rank 3) is the **leading axis**: operations on rank 3 selections distribute per-buffer first, then per-range within each buffer. ### Trains -TODO +A **train** is a sequence of functions that compose according to specific rules, following [APL train syntax](https://aplwiki.com/wiki/Train). + +**2-train (Atop)** `F G`: +- Monadic: `(F G) ω = F (G ω)` +- Dyadic: `α (F G) ω = F (α G ω)` + +**3-train (Fork)** `F G H`: +- Monadic: `(F G H) ω = (F ω) G (H ω)` +- Dyadic: `α (F G H) ω = (α F ω) G (α H ω)` -## 1.2 The Editor State Monad +**Longer trains**: Parsed right-associatively. A train of n functions is parsed by taking the rightmost 3 functions as a fork (if n is odd and ≥3) or the rightmost 2 functions as atop (if n is even), then recursively parsing the remaining functions as the left part. For example: +- 4 functions `F G H I` → `F (G H I)` (atop of F with a fork) +- 5 functions `F G H I J` → `(F G) (H I J)` (atop of an atop with a fork) + +### Hooks + +RPRT provides two explicit hook combinators from BQN for flexible function composition: + +**Before (Left Hook)** `F>G`: +- Monadic: `(F>G) ω = (F ω) G ω` +- Dyadic: `α (F>G) ω = (F α) G ω` + +**After (Right Hook)** `F<G`: +- Monadic: `(F<G) ω = ω F (G ω)` +- Dyadic: `α (F<G) ω = α F (G ω)` + +These combinators bind more tightly than trains and enable partial application patterns. + +## 1.3 The Editor State Monad All RPRT expressions evaluate within an implicit `EditorState` monad containing: - Buffer contents (for all open buffers) @@ -49,20 +110,7 @@ All RPRT expressions evaluate within an implicit `EditorState` monad containing: Selection and Text functions have signatures `EditorState Selection` and `EditorState Text` respectively. -## 1.3 Type Coercion - -When a Text argument is required, a Selection is implicitly coerced to Text by extracting its contents. This coercion has no side effects. - -| Input rank | Coerced Text | -|------------|------------------------------------------------------------------| -| 0 | Empty string (position has no extent) | -| 1 | Contents of the range | -| 2 | Contents of each range (text function broadcasts per range) | -| 3 | Contents per buffer per range (broadcasts per buffer then range) | - -For ranks 2 and 3, the selection provides multiple text values. - -## 1.4 Evaluation Contexts +## 1.5 Evaluation Contexts **Top-level Selection:** Sets the editor's current selection. ``` @@ -83,7 +131,7 @@ b "name" # Switch current buffer to "name" The `b` statement is not a function and cannot be composed. It only appears as a top-level statement. -## 1.5 The Grouping Operator `{}` +## 1.6 The Grouping Operator `{}` The grouping operator `{}` provides transactional semantics: all operations within see the same input editor state, and their modifications are merged with position tracking where possible. Any error aborts the entire evaluation. @@ -94,7 +142,7 @@ The grouping operator `{}` provides transactional semantics: all operations with - All operations execute with the original positions - Returns the union of all resulting Selections -# 1.6 Composition and Parsing +# 1.7 Composition and Parsing **Precedence:** Following BQN rules: - Operators bind tighter than functions @@ -122,7 +170,7 @@ There are ten core selection functions in RPRT. All ten have nullary and monadic | `B/re/` | All buffers whose name match re, mapping to their contents | 3 | | `-` | Entire buffer (0-$) | 2 | -In the monadic case, all defined functions are rank preserving. Their action on ranks > 1 are given by broadcasting, that is, `α F` where `α` has rank 2 is computed by applying `F` to each rank 1 range in `α` and collecting the results into a list of ranges, mutatis mutandis for rank 3. +In the monadic case, functions have distinct rank 0 and rank 1 semantics. For ranks 2-3, the rank 1 semantics vectorise across ranges (see §1.2 for broadcasting and vectorisation). | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |-----------|----------------------------------|------------------------------------------| @@ -154,9 +202,33 @@ The span function `-` is the only selection function with dyadic semantics. It c ## 2.2 Selection Function Operators -Operators take functions as arguments and return new functions. +Operators take functions as arguments and return new functions. Selection function operators fall into two categories which compose in a restricted manner: -### 2.2.1 The Reverse Operator `'` +**Search Modifiers** (at most one allowed): +- _(none)_ — Unmodified search semantics (see §2.1) +- `'` — Reverse (§2.2.2) +- `;` — Sequential (§2.2.3) +- `';` — Reverse-Sequential +- `;'` — Sequential-Reverse + +**Result Transformations** (at most one allowed): +- _(none)_ — Unmodified result +- `~` — Complement (§2.2.4) +- `?` — Conditional (§2.2.5) +- `~?` — Complement-Conditional +- `?~` — Conditional-Complement + +A fully-specified selection function has the form: + +``` +[result transformation] [search modifier] [base function] +``` + +where both operators are optional. Order matters: `';F` is not `;'F` and `~?F` is not `?~F`. + +### 2.2.1 Search modifiers + +#### The Reverse Operator `'` The reverse operator `'` takes a selection function and reverses its direction. @@ -172,7 +244,7 @@ The reverse operator `'` takes a selection function and reverses its direction. | `'x/re/` | All matches, right-to-left | 2 | | `'+n` | Line -n in buffer | 1 | -In the monadic case, all functions are rank preserving, and all of the above functions are the identity on rank 0 inputs. Their action on ranks > 1 is by broadcasting. +In the monadic case, all functions are rank preserving, and all of the above functions are the identity on rank 0 inputs. For ranks 2-3, the rank 1 semantics vectorise (see §1.2). | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |------------|--------------------------|----------------------------------| @@ -185,7 +257,7 @@ In the monadic case, all functions are rank preserving, and all of the above fun | `α '+n` | `α` | Line -n within `α` | -### 2.2.2 The Sequential Operator `;` +#### The Sequential Operator `;` The sequential operator `;` transforms a selection function from operating "within" a selection to operating "starting from the end" of that selection. @@ -203,7 +275,23 @@ The sequential operator `;` transforms a selection function from operating "with | `α ;x/re/` | All matches forward from `α` | All matches forward from end of `α` | | `α ;+n` | Line n after `α` | Line n after end of `α` | -### 2.2.3 The Complement Operator `~` +#### Combined Search Modifiers + +The reverse and sequential operators may be composed in either order, producing distinct semantics: + +**`;'F` (Sequential-Reverse):** +- Start from the end boundary of the selection +- Search backward (in reverse direction) + +**`';F` (Reverse-Sequential):** +- Start from the start boundary of the selection +- Search backward (in reverse direction) + +The key distinction is the starting position: `;'F` begins at the end and searches backward, while `';F` begins at the start and searches backward. + +### 2.2.2 Result transformations + +#### The Complement Operator `~` The complement operator `~` takes a selection function and produces a new selection function which returns the complement. Naturally this changes rank somewhat, as explained in the below table. @@ -214,13 +302,39 @@ The complement operator `~` takes a selection function and produces a new select | 2 | 2 | The selection corresponding to the ranges between the selections | | 3 | 3 | The broadcast of `~` to each buffer | +#### The Conditional Operator `?` + +The conditional operator `?` takes a selection function and produces a new selection function with the following semantics. + +| Expression | Semantics | +|-----------------|--------------------------------------------------| +| Nullary `?F` | exactly as `F` | +| Monadic `α ?F` | if `α F` is non-empty then `α` otherwise empty | +| Dyadic `α ?F β` | if `α F β` is non-empty then `α` otherwise empty | + +#### Combined Result Transformations + +The complement and conditional operators may be composed in either order, producing distinct semantics: + +**`~?F` (Complement-Conditional):** +1. Evaluate function `F` to obtain result `α` +2. Apply conditional: if `α` is non-empty, return input selection; otherwise return empty +3. Apply complement to the conditional result + +**`?~F` (Conditional-Complement):** +1. Evaluate function `F` to obtain result `α` +2. Apply complement to `α` to obtain `β` +3. Apply conditional to `β` + +The key distinction is when the complement is applied: `~?F` complements the conditional result, while `?~F` applies conditional to the complemented result of `F`. + # 3. Text Functions ## 3.1 Text Operations Text operations have signature `Selection × Text → Selection`. They modify the editor state (buffer contents) and return the affected Selection. -Text operations use [leading axis theory](https://aplwiki.com/wiki/Leading_axis_theory) where buffer names are the leading axis: TODO TODO TODO if `α F β` where `β` is rank 2 but `α` is rank 1, then operation applies once per range in `β`, using the contents of each range as the text argument. +Text operations follow the broadcasting rules described in §1.2. When the text argument `τ` is itself a Selection that coerces to Text, the text values are paired with ranges following the buffer-first, then range-within-buffer ordering (the leading axis). | Function | Effect | Return selection | |----------|--------------------------------------------------------------|----------------------| @@ -259,21 +373,35 @@ Text operations use [leading axis theory](https://aplwiki.com/wiki/Leading_axis_ **Examples:** -TODO: throw some combinators in to make this example nicer. - ``` -| "date" # Run date with no input, returns selection to output -/pattern/ c (| "date") # Replace pattern with date command output -selection c (selection | "sort") # Pipe selection through sort, replace with result +| "date" # Run date with no input, returns selection to output +/pattern/ c (| "date") # Replace pattern with date command output +α c<(|<"sort") # Pipe selection through sort, replace with result (using After hooks) ``` -An idiom +The third example composes two After hooks (§1.2): `|<"sort"` creates a monadic selection function that pipes its argument through sort, where `"sort"` is treated as a constant Text function (§1.1). Then `α c<(|<"sort")` expands to `α c (α | "sort")`, avoiding explicit repetition of the selection. Note the parentheses are necessary: `c<|<"sort"` would incorrectly parse as `α c (| "sort")`, running sort with no input. + +## 3.2 Text Function Operators + +Text function operators modify the behavior of text functions. + +### The Swap Operator + +The `swap` operator reverses the arguments of a text function, converting `α F τ` into `τ F α`. For this to typecheck both arguments must be selections. + +**Syntax:** `@ F` + +**Semantics:** `α (@ F) τ = τ F α` + +This is particularly useful for composing text operations where the natural argument order needs to be reversed. + +**Idiom for moving content:** ``` -α {swap i , d} β +α {@i , d} β ``` -Evaluates in parallel -* `α (swap i) β` which is `β i α` which means "insert at (the start of) β the content from α" +Using the swap operator `@`, this evaluates in parallel: +* `α @i β` which is `β i α` which means "insert at (the start of) β the content from α" * `α d β` which means "delete α" The net result of this is to _move_ the content of α to (the start of) the selection given by β. |
