diff options
| author | Marshall Lochbaum <mwlochbaum@gmail.com> | 2021-07-24 22:47:46 -0400 |
|---|---|---|
| committer | Marshall Lochbaum <mwlochbaum@gmail.com> | 2021-07-24 22:47:46 -0400 |
| commit | a17782ce2ec31709ce30edb3d96fe2f3a9a6ed1f (patch) | |
| tree | b601681b2282f1a51042f8faf5bfe0e0242c0c31 /doc | |
| parent | 436bf368830c828f8008bf55632e2bb4c2a2578f (diff) | |
Documentation on fill elements
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/README.md | 1 | ||||
| -rw-r--r-- | doc/arrayrepr.md | 4 | ||||
| -rw-r--r-- | doc/embed.md | 2 | ||||
| -rw-r--r-- | doc/fill.md | 65 | ||||
| -rw-r--r-- | doc/match.md | 2 | ||||
| -rw-r--r-- | doc/order.md | 2 | ||||
| -rw-r--r-- | doc/pick.md | 4 | ||||
| -rw-r--r-- | doc/reshape.md | 4 | ||||
| -rw-r--r-- | doc/shift.md | 2 | ||||
| -rw-r--r-- | doc/take.md | 4 | ||||
| -rw-r--r-- | doc/types.md | 2 |
11 files changed, 79 insertions, 13 deletions
diff --git a/doc/README.md b/doc/README.md index 8841f221..763fdfbf 100644 --- a/doc/README.md +++ b/doc/README.md @@ -20,6 +20,7 @@ Concepts: - [Based array theory](based.md) - [Array notation and display](arrayrepr.md) - [Array indices](indices.md) +- [Fill elements](fill.md) - [The leading axis model](leading.md) - [Function trains](train.md) - [Blocks](block.md) (including function and modifier definition) diff --git a/doc/arrayrepr.md b/doc/arrayrepr.md index 74e8426e..5f33bcd6 100644 --- a/doc/arrayrepr.md +++ b/doc/arrayrepr.md @@ -18,7 +18,7 @@ Although it's really part of the language environment and not BQN itself, let's There are several different ways to show arrays: as a string `""`, with brackets `⟨⟩`, or with corners `┌` and `┘`. We'll start with the most general, the corners. These show arrays of any rank while the other two ways are special cases for lists. -Array displays show only the array shape and elements. The fill is an inferred property and the display never indicates or depends on it. +Array displays show only the array shape and elements. The [fill](fill.md) is an inferred property and the display never indicates or depends on it. ### Corners @@ -88,7 +88,7 @@ This case also covers empty lists, which are shown as `⟨⟩`. This includes an *The tutorial section [here](../tutorial/list.md#list-notation) also covers this topic.* -There are three kinds literal notation for lists: strings, list notation, and stranding. Strings indicate character lists (with space for the fill) and the other two can combine any sequence of elements. +There are three kinds literal notation for lists: strings, list notation, and stranding. Strings indicate character lists (with space for the [fill](fill.md)) and the other two can combine any sequence of elements. ### Strings diff --git a/doc/embed.md b/doc/embed.md index f98b6fdd..d75275a0 100644 --- a/doc/embed.md +++ b/doc/embed.md @@ -44,7 +44,7 @@ You can also use an array to pass multiple functions or other values from JS int In the programs above we've used numbers and functions of one argument, which mean the same thing in BQN and JS. This isn't the case for all types: although every BQN value is stored as some JS value, the way it's represented may not be obvious and there are many JS values that don't represent any BQN value and could cause errors. BQN operations don't verify that their inputs are valid BQN values (this would have a large performance cost), so it's up to the JS programmer to make sure that values passed in are valid. To do this, you need to know the encodings for each of the seven BQN [types](types.md) you're going to use. -The two atomic data values are simple: numbers are just JS numbers, and characters are strings containing a single code point. Arrays *are* JS arrays, but with some extra information. Since JS arrays are 1-dimensional, a BQN array `a` is stored as the element list `⥊a`. Its shape `≢a`, a list of numbers, is `a.sh` in JS (the shape isn't necessarily a BQN array so it doesn't have to have a `sh` property). Optionally, its fill element is `a.fill`. Note that a BQN string is not a JS string, but instead an array of BQN characters, or JS strings. To convert it to a JS string you can use `str.join("")`. +The two atomic data values are simple: numbers are just JS numbers, and characters are strings containing a single code point. Arrays *are* JS arrays, but with some extra information. Since JS arrays are 1-dimensional, a BQN array `a` is stored as the element list `⥊a`. Its shape `≢a`, a list of numbers, is `a.sh` in JS (the shape isn't necessarily a BQN array so it doesn't have to have a `sh` property). Optionally, its [fill element](fill.md) is `a.fill`. Note that a BQN string is not a JS string, but instead an array of BQN characters, or JS strings. To convert it to a JS string you can use `str.join("")`. There are two utilities for converting from JS to BQN data: `list([…])` converts a JS array to a BQN list, and `str("JS string")` converts a string. diff --git a/doc/fill.md b/doc/fill.md new file mode 100644 index 00000000..45d98487 --- /dev/null +++ b/doc/fill.md @@ -0,0 +1,65 @@ +*View this file with results and syntax highlighting [here](https://mlochbaum.github.io/BQN/doc/fill.html).* + +# Fill elements + +A few array operations need an array element to use when no existing element applies. BQN tries to maintain a "default" element for every array, known as a fill element, for this purpose. If it's known, the fill element is a nested array structure where each atom is either `0` or `' '`. If no fill is known, a function that requests it results in an error. + +Fills are used by [Take](take.md) (`↑`) when a value in `𝕨` is larger than the corresponding length in `𝕩`, by the two [Nudge](shift.md) functions (`»«`) when `𝕩` is non-empty, and by [First](pick.md) (`⊑`) and [Reshape](reshape.md) (`⥊`) when `𝕩` is empty. Except for these specific cases, the fill value an array has can't affect the program. The result of [Match](match.md) (`≡`) doesn't depend on fills, and any attempt to compute a fill can't cause side effects. + +## Using fills + +For the examples in this section we'll use the fact that an all-number array usually has `0` as a fill while a string has `' '` (BQN maintains fills alongside array values rather than deriving them from arrays, so it's possible to construct arrays where this isn't true, but this probably wouldn't happen in ordinary code). + +[Take](take.md) (`↑`) and [Nudge](shift.md) (`»«`) in either direction use the fill for padding, to extend the array past its boundary. For example, `𝕨↑𝕩` will add elements to one side when a number in `|𝕨` is larger than the corresponding length in `≢𝕩`. + + ¯7 ↑ 4⥊3 # Fill with 0 + + ¯7 ↑ "qrst" # Fill with space + +Nudge Left or Right shifts the array over and places a fill in the vacated space, effectively extending it backwards by one. If `𝕩` is empty then it shouldn't give an error, but it's safer not to rely on this. + + »¨ ⟨4⥊3,"qrst"⟩ + + 3↑⟨⟩ # Fill unknown + + »⟨⟩ # Fill not needed + +[First](pick.md) (`⊑`) and [Reshape](reshape.md) (`⥊`) use the fill when `𝕩` is empty, and in the case of Reshape only when the result needs to be non-empty. + + ⊑ "" + + 4 ⥊¨ ⟨↕0, ""⟩ + + 0‿3 ⥊ ⟨⟩ # Fill not needed + +If for some reason you need to find an array's fill element, the easiest way is `⊑0⥊a`. + + ⊑0⥊"string" + +## How fills are computed + +For the exact requirements placed on fill, see [the specification](../spec/inferred.md#fill-elements) (particularly "required functions"). This section loosely describes behavior in existing BQN implementations, and includes some parts that aren't required in the specification. + +A fill element should encompass something that's necessarily true for all elements of an array. If the way an array is computed implies it's all numbers, the fill should be `0`. If every element is a list of two numbers, then the fill should be `⟨0,0⟩`. If every element is a list but the lengths might vary, `⟨⟩` is probably a reasonable fill element. + +For [arithmetic](arithmetic.md) primitives, the fill is found by the rules of pervasion, applying the function to both argument fills. Generally this means it consists of `0`, but character arithmetic also allows space fills. + + » "abc" + 4‿3‿2 + +[Mapping](map.md) modifiers Each and Table (`¨⌜`) might try to follow a similar strategy, applying `𝔽` to argument fills to obtain the result fill. The absolute rule here is that this computation cannot cause side effects or an error, so for a complicated `𝔽` such as a block function this procedure is likely to be aborted to avoid disrupting the rest of the program. + +Most other primitives fit in one of three broad categories as shown in the table below. Structural primitives, indicated by `⊢`, don't change the fill of `𝕩`. Combining structural primitives, indicated by `∩`, only depend on the fill of all combined arrays—elements of `𝕩` in the one-argument case, or `𝕨` and `𝕩` in the two-argument case. Finally, many functions such as [search functions](search.md) return only numbers and have a fill of `0`. + +| Fill | Monads | Dyads | Modifiers +|--------|--------------|-------------|---------- +| `⊢` | `∧∨⥊≍»«⌽⍉⊏⍷` | `⥊↑↓↕⌽⍉/⊏` | `` 𝔽` `` +| `∩` | `>∾` | `∾≍»«` +| `0` | `≢/⍋⍒∊⊐⊒` | `⍋⍒⊐⊒∊⍷` + +Besides these, there are a few primitives with special fills. [Enclose](enclose.md) (`<`) uses a fill derived directly from `𝕩`, with all numbers replaced by `0` and characters by `' '` (if it contains non-data atoms, the fill doesn't exist). [Range](range.md) (`↕`) does the same, although the reason is less obvious: the result elements don't match `𝕩`, but they have the same structure. + +[Prefixes and Suffixes](prefixes.md) (`↑↓`) use `0↑𝕩` for the fill, as do [Group](group.md) and Group Indices (`⊔`) in the single-axis case. Fills for multi-axis `⊔` are more complicated, but follow the rule that variable-length axes are changed to length 0. The *elements* of the result of `⊔` also have a fill specified: the same as `𝕩` for Group, or `0` for Group Indices. + + 6 ↑ ↑↕3 # Two fills at the end + + »¨ 3‿4‿1 /⊸⊔ "abc0123A" diff --git a/doc/match.md b/doc/match.md index 5912b292..e85b8c59 100644 --- a/doc/match.md +++ b/doc/match.md @@ -15,7 +15,7 @@ Match always gives the same result as [Equals](arithmetic.md#comparisons) (`=`) "abc" = "ab" # Mismatched shapes "abc" ≡ "ab" -Match compares arrays based on their fundamental properties—[shape](shape.md) and elements—and not the [fill element](../spec/inferred.md#fill-elements), which is an inferred property. Since it can be computed differently in different implementations, using the fill element in Match could lead to some confusing results. Even if the implementation doesn't define a fill for `'a'‿'b'‿'c'`, it should still be considered to match `"abc"`. +Match compares arrays based on their fundamental properties—[shape](shape.md) and elements—and not the [fill element](fill.md), which is an inferred property. Since it can be computed differently in different implementations, using the fill element in Match could lead to some confusing results. Even if the implementation doesn't define a fill for `'a'‿'b'‿'c'`, it should still be considered to match `"abc"`. To give a precise definition, two arrays are considered to match if they have the same shape and all corresponding elements from the two arrays match. Every array has a finite [depth](depth.md) so this recursive definition always ends up comparing non-arrays, or atoms. An array never matches an atom, so the result if only one argument is an atom is `0`. The interesting case is when both arguments are atoms, discussed below. diff --git a/doc/order.md b/doc/order.md index 77e3a7c8..93c0c1c7 100644 --- a/doc/order.md +++ b/doc/order.md @@ -20,7 +20,7 @@ You've probably seen it before. Sort Up (`∧`) reorders the major cells of its ∨ "δαβγ" -Sort Down always [matches](match.md) Sort Up [reversed](reverse.md), `⌽∘∧`. The reason for this is that BQN's array ordering is a [total order](https://en.wikipedia.org/wiki/Total_order), meaning that if one array doesn't come earlier or later that another array in the ordering then the two arrays match. Since any two non-matching argument cells are strictly ordered, they will have one ordering in `∧` and the opposite ordering in `∨`. With the reverse, any pair of non-matching cells are ordered the same way in `⌽∘∧` and `∨`. Since these two results have the same major cells in the same order, they match. However, note that the results will not always behave identically because Match doesn't take fill elements into account (if you're curious, take a look at `⊑¨∨⟨↕0,""⟩` versus `⊑¨⌽∘∧⟨↕0,""⟩`). +Sort Down always [matches](match.md) Sort Up [reversed](reverse.md), `⌽∘∧`. The reason for this is that BQN's array ordering is a [total order](https://en.wikipedia.org/wiki/Total_order), meaning that if one array doesn't come earlier or later that another array in the ordering then the two arrays match. Since any two non-matching argument cells are strictly ordered, they will have one ordering in `∧` and the opposite ordering in `∨`. With the reverse, any pair of non-matching cells are ordered the same way in `⌽∘∧` and `∨`. Since these two results have the same major cells in the same order, they match. However, note that the results will not always behave identically because Match doesn't take [fill elements](fill.md) into account (if you're curious, take a look at `⊑¨∨⟨↕0,""⟩` versus `⊑¨⌽∘∧⟨↕0,""⟩`). ## Grade diff --git a/doc/pick.md b/doc/pick.md index 425b8cf9..9b014796 100644 --- a/doc/pick.md +++ b/doc/pick.md @@ -4,7 +4,7 @@ Pick (`⊑`) chooses elements from `𝕩` based on [index](indices.md) lists from `𝕨`. `𝕨` can be a plain list, or even one number if `𝕩` is a list, in order to get one element from `𝕩`. It can also be an array of index lists, or have deeper array structure: each index list will be replaced with the element of `𝕩` at that index, effectively applying to `𝕨` at [depth](depth.md#the-depth-modifier) 1. -With no `𝕨`, monadic `⊑𝕩` takes the first element of `𝕩` in index order, or its fill element if `𝕩` is empty (causing an error if no fill is known). +With no `𝕨`, monadic `⊑𝕩` takes the first element of `𝕩` in index order, or its [fill element](fill.md) if `𝕩` is empty (causing an error if no fill is known). While sometimes "scatter-point" indexing is necessary, using Pick to select multiple elements from `𝕩` is less array-oriented than [Select](select.md) (`⊏`), and probably slower. Consider rearranging your data so that you can select along axes instead of picking out elements. @@ -44,7 +44,7 @@ With no left argument, `⊑` is called First, and performs a slight generalizati ⊑ "First" ⊑ ↕4‿2‿5‿1 -If `𝕩` is empty then Pick always results in an error. First never gives an error: instead it returns the fill element for `𝕩`. +If `𝕩` is empty then Pick always results in an error. First never gives an error: instead it returns the [fill element](fill.md) for `𝕩`. ⊑ "" ⊑ ≢π diff --git a/doc/reshape.md b/doc/reshape.md index e9f518d2..85c1f7a3 100644 --- a/doc/reshape.md +++ b/doc/reshape.md @@ -88,7 +88,7 @@ If the left argument implies a smaller number of elements, then only the initial 3‿3 ⥊ a -If the left argument implies a larger number of elements, then the argument elements are reused cyclically. Below, we reach the last element `247` and start over at `135`. If the array doesn't have any elements to start with, its fill value is used instead, but it's probably best not to invoke this case! +If the left argument implies a larger number of elements, then the argument elements are reused cyclically. Below, we reach the last element `247` and start over at `135`. If the array doesn't have any elements to start with, its [fill element](fill.md) is used instead, but it's probably best not to invoke this case! 15 ⥊ a @@ -111,7 +111,7 @@ Above, the length given is `∘`, a special value that indicates that a length t - `∘` says the length must be an exact fit, and gives an error in such a case. - `⌊` rounds the length down, so that some elements are discarded. - `⌽` rounds the length up, repeating elements to make up the difference. -- `↑` rounds the length up, but uses the argument's fill values for the needed extra elements. +- `↑` rounds the length up, but uses the argument's fill for the needed extra elements. These values are just BQN primitives of course. They're not called by Reshape or anything like that; the primitives are just chosen to suggest the corresponding functionality. diff --git a/doc/shift.md b/doc/shift.md index d20d4eff..cc582fae 100644 --- a/doc/shift.md +++ b/doc/shift.md @@ -9,7 +9,7 @@ The result of a shift function always has the same shape as `𝕩`. The function 0‿0 » 3‿2‿1 # Shift Before "end" « "add to the " # Shift After -The cells to add come from `𝕨` if it's present, as shown above. Otherwise, a single cell of fill values for `𝕩` is used. This kind of shift, which moves cells in `𝕩` over by just one, is called a "nudge". +The cells to add come from `𝕨` if it's present, as shown above. Otherwise, a single cell of [fill elements](fill.md) for `𝕩` is used. This kind of shift, which moves cells in `𝕩` over by just one, is called a "nudge". » "abcd" # Nudge « 1‿2‿3 # Nudge Back diff --git a/doc/take.md b/doc/take.md index 8a8d6423..d90f4055 100644 --- a/doc/take.md +++ b/doc/take.md @@ -46,7 +46,7 @@ The basic idea of Take (`↑`) is to get the first few elements of a list, while - `𝕩` can be an atom, or array of any rank (the result will be an array). - `𝕨` can be negative to take or drop from the end instead of the beginning. -- For Take, if `𝕨` is larger than the length of `𝕩`, then fills are added. +- For Take, if `𝕨` is larger than the length of `𝕩`, then [fills](fill.md) are added. - `𝕨` can have multiple numbers corresponding to leading axes of `𝕩`. - `𝕨` is allowed to be longer than the rank of `𝕩`; `𝕩` will be extended to fit. @@ -63,7 +63,7 @@ Let's start with a natural number `𝕨`. Take gives the first `𝕨` major cell 1 ↓ >"maj"‿"orc"‿"ell" -If `𝕨` is too large it's usually not a problem. For Take, fill elements are added to the end to bring `𝕩` up to the required length—although this *will* fail if `𝕩` has no fill element. For Drop, the result is an empty array. +If `𝕨` is too large it's usually not a problem. For Take, [fill elements](fill.md) are added to the end to bring `𝕩` up to the required length—although this *will* fail if `𝕩` has no fill element. For Drop, the result is an empty array. ↕6 diff --git a/doc/types.md b/doc/types.md index 2564d698..8d51c5bc 100644 --- a/doc/types.md +++ b/doc/types.md @@ -46,7 +46,7 @@ FS ← {𝕩 Enc˜ "g"Attr⟨"font-size",(Fmt𝕨)∾"px"⟩} The reason operations and namespaces are called "mutable" is that the values obtained from them—by calling an operation on particular arguments or reading a field from a namespace—may change over the course of the program. This property is caused by variable modification `↩`, which can directly change a namespace field, or change the behavior of an operation that uses the modified variable. This means that a program that doesn't include `↩` won't have such changes in behavior. However, there will still be an observable difference between immutable data and the mutable types: code that creates a mutable value (for example, a block function `{𝕩}`) creates a different one each time, so that two different instances don't [match](match.md) (`≡`) each other. Data values created at different times may match, but mutable values never will. -An array is considered immutable because its shape, and what elements it contains, cannot change. An array has no identity outside these properties (and possibly its fill element), so an array with a different shape or different elements would simply be a different array. However, any element of an array could be mutable, in which case the behavior of the array would change with respect to the operation of selecting that element and calling it or accessing a field. +An array is considered immutable because its shape, and what elements it contains, cannot change. An array has no identity outside these properties (and possibly its [fill element](fill.md)), so an array with a different shape or different elements would simply be a different array. However, any element of an array could be mutable, in which case the behavior of the array would change with respect to the operation of selecting that element and calling it or accessing a field. ## Data types |
