# Rank PolymoRphic Text editor (RPRT) A compositional, point-free language for text manipulation. # 1. Core Concepts ## 1.1 Two Fundamental Types All functions work with two core types: 1. **Selection** - addresses and regions in buffers 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 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 Ranks 0-2 operate within a single buffer (typically the current buffer). Rank 3 is distinguished by spanning multiple buffers simultaneously. 2. **Text** - string data Text has three ranks: - **Rank 1**: A single string - **Rank 2**: A list of strings - **Rank 3**: A mapping from buffer identifiers to lists of strings Unlike Selection, Text has no meaningful need to distinguish rank 0: a single character is simply a rank 1 string of length one. ### Function Types A **Selection function** is something of signature `Selection × Selection → Selection`. A **Text function** is something of signature `Text × Selection → 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). ### 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 | The character at that position, or empty string at end of buffer | | 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) 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 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`: - Niladic: `F G` = (F) G`; `F` is evaluated niladically and `G` is evaluated monadically on the result - Monadic: `ω (F G) = (ω F) G` - Dyadic: `ω (F G) α = (ω F α) G` **3-train (Fork)** `F G H`: - Niladic: `F G H` = `(F) G (H)`; `F`, `H` are evaluated niladically and `G` is evaluated dyadically on the result - Monadic: `ω (F G H) = (ω F) G (ω H)` - Dyadic: `ω (F G H) α = (ω F α) G (ω H α)` **Longer trains**: Parsed left-associatively. A train of n functions is parsed by taking the leftmost 3 functions as a fork (if n is odd and ≥3) or the leftmost 2 functions as atop (if n is even), then recursively parsing the remaining functions as the right part. For example: - 4 functions `F G H I` → `(F G H) I` (I atop a fork) - 5 functions `F G H I J` → `(F G H) (I J)` (atop, atop a fork) ### Hooks RPRT provides two explicit hook combinators from BQN for flexible function composition: **Before (Left Hook)** `F>G`: - Niladic: `F>G = (F) G`; `F` is evaluated niladically and `G` is evaluated monadically on the result - Monadic: `ω (F>G) = (ω F) G ω` - Dyadic: `ω (F>G) α = (ω F) G α` **After (Right Hook)** `F/re/` or `./re/` In the niladic form this is `(.) /re/` (left hook semantics §1.2, or left associativity), and `.` niladically is the current selection (§3.1). Thus both `.>/re/` and `./re/` apply `/re/` to the current selection. ## 2.2 Selection Function Operators Operators take functions as arguments and return new functions. Selection function operators fall into two categories which compose in a restricted manner: **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. **Note:** `'F` is not defined when `F` is one of the functions `B-` . | Function | Nullary | Rank | |----------|--------------------------------|------| | `'e` | synonymous with `e` | 2 | | `'$` | Start of buffer (position 0) | 0 | | `'#n` | Character n from end of buffer | 0 | | `'n` | Line n from end of buffer | 1 | | `'/re/` | First match backward from end | 1 | | `'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. For ranks 2-3, the rank 1 semantics vectorise (see §1.2). | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |------------|--------------------------|----------------------------------| | `α 'e` | Empty selection (rank 2) | Empty selection (rank 2) | | `α '$` | `α` | Start position of `α` | | `α '#n` | `α` | Char n from end of `α` | | `α 'n` | `α` | Line n from end of `α` | | `α '/re/` | `α` | First match backward within `α` | | `α 'x/re/` | `α` | All matches right-to-left in `α` | | `α '+n` | `α` | Line -n within `α` | #### The Sequential Operator `;` The sequential operator `;` transforms a selection function from operating "within" a selection to operating "starting from the end" of that selection. **Note:** It is erroneous to use `;F` in the nullary case. TODO: or are there interesting semantics? **Note:** `;F` is not defined when `F` is one of `B-` . | Function | Semantics for rank 0 `α` | Semantics for rank 1 `α` | |------------|------------------------------|-------------------------------------| | `α ;e` | Empty selection (rank 2) | Empty selection (rank 2) | | `α ;$` | End of buffer | End of buffer (ignores `α`) | | `α ;#n` | Character n after `α` | Character n after end of `α` | | `α ;n` | Line n after `α` | Line n after end of `α` | | `α ;/re/` | First match forward from `α` | First match forward from end of `α` | | `α ;x/re/` | All matches forward from `α` | All matches forward from end of `α` | | `α ;+n` | Line n after `α` | Line n after end of `α` | #### 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. | Rank of output of `F` | Rank of output of `~F` | Semantics | |-----------------------|------------------------|------------------------------------------------------------------| | 0 | 2 | The complement of the position its buffer as a selection | | 1 | 2 | The complement of the range in its buffer as a selection | | 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 `Text × Selection → Selection`. They modify the editor state (buffer contents) and return the affected Selection. 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 | |----------|--------------------------------------------------------------|----------------------| | `.` | No-op | Current selection | | `c` | No-op | Current selection | | `i` | No-op | Current selection | | `a` | No-op | Current selection | | `d` | Delete current selection | Empty selection | | `w` | Write the current buffer to its associated file. | Current selection | | `\|` | Run current selection as shell commands, replace with output | Selection of outputs | **Note:** `\|` is undefined when the rank of the current selection is 0. | Function | Effect | Return selection | |----------|--------------------------------------------------|---------------------------------------------| | `τ .` | No-op | Current selection in buffers named by `τ` | | `τ c` | No-op | Replace the current selection by `τ` | | `τ i` | No-op | Insert `τ` into the current selection | | `τ a` | No-op | Append `τ` to the current selection | | `τ d` | Delete current selection in buffers named by `τ` | Empty selection in buffers named by `τ` | | `τ w` | Write the buffers named by `τ`. | Current selection in buffers named by `τ` | | `τ \|` | Run `τ` | Return selection of ephemeral buffer output | **Note:** The text argument `τ` may be a string literal or a Selection (which coerces to Text). | Function | Effect | Semantics | |----------|------------------------------------------------------|-----------------------------------------------------------| | `τ . α` | No-op | Current selection in buffer(s) named by `τ` (`α` ignored) | | `τ c α` | No-op | Change (replace) `α` with text `τ` | | `τ i α` | No-op | Insert text `τ` before `α` | | `τ a α` | No-op | Append text `τ` after `α` | | `τ d α` | Delete `α` in buffers named by `τ` | Empty selection in buffers nameb by `τ` | | `τ w α` | Write (concatenation of) `α` in buffers named by `τ` | `α` | | `τ \| α` | Pipe `α` through command `τ` | Return selection of ephemeral output buffer | **Examples:** ``` "date"| # Run date with no input, returns selection to output ("date" |) c /pattern/ # Replace pattern with date command output α ("sort">|)>c # Pipe selection through sort, replace with result (using After hooks) ``` The third example composes two After hooks (§1.2): `"sort>|"` creates a monadic function that pipes its argument through sort, where `"sort"` is treated as a constant Text function (§1.1). Then `α ("sort">|)>c` expands to `(α ("sort">|)) c α` = `("sort" | α) c α`, avoiding explicit repetition of the selection. Note that parentheses are not necessary because hooks are right-associative. ## 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 type-check 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:** ``` α {i , d} β ``` Using the swap operator `@`, this evaluates in parallel: * `α i β` which is "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 β.