From a17782ce2ec31709ce30edb3d96fe2f3a9a6ed1f Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sat, 24 Jul 2021 22:47:46 -0400 Subject: Documentation on fill elements --- docs/doc/arrayrepr.html | 4 +-- docs/doc/embed.html | 2 +- docs/doc/fill.html | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/doc/glossary.html | 2 +- docs/doc/index.html | 1 + docs/doc/match.html | 2 +- docs/doc/order.html | 2 +- docs/doc/pick.html | 4 +-- docs/doc/reshape.html | 4 +-- docs/doc/shift.html | 2 +- docs/doc/take.html | 4 +-- docs/doc/types.html | 2 +- 12 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 docs/doc/fill.html (limited to 'docs') diff --git a/docs/doc/arrayrepr.html b/docs/doc/arrayrepr.html index fa2332c6..4c7f8268 100644 --- a/docs/doc/arrayrepr.html +++ b/docs/doc/arrayrepr.html @@ -30,7 +30,7 @@ ┘

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 is an inferred property and the display never indicates or depends on it.

Corners

Those top-left and bottom-right corners are a distinctive part of BQN's display, as other systems almost always completely enclose the contents. BQN could add the other two corners, naturally; it just doesn't. Within the corners, elements are separated by whitespace only, and generally aligned to the top left.

↗️
    2,"xy"22"abcd",4  # Nested 2×2 array
@@ -160,7 +160,7 @@
 

This case also covers empty lists, which are shown as ⟨⟩. This includes an empty string, as the only difference between an empty string and any other empty list is its fill element and array displays don't depend on the fill.

List literals

The tutorial section here 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) and the other two can combine any sequence of elements.

Strings

A string consists of a sequence of characters surrounded by double quotes "". The only rule for the characters inside is that any double quote must be escaped by repeating it twice; otherwise the string ends at that point.

↗️
    "-'×%""*"
diff --git a/docs/doc/embed.html b/docs/doc/embed.html
index 4b4e0365..31a5f22e 100644
--- a/docs/doc/embed.html
+++ b/docs/doc/embed.html
@@ -34,7 +34,7 @@
 

You can also use an array to pass multiple functions or other values from JS into BQN all at once. However, a JS array can't be used directly in BQN because its shape isn't known. The function list() converts a JS array into a BQN list by using its length for the shape; the next section has a few more details.

JS encodings

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 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 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.

Operations are all stored as JS functions, with one or two arguments for the inputs. The type is determined by the .m property, which is 1 for a 1-modifier and 2 for a 2-modifier, and undefined or falsy for a function. Functions might be called with one or two arguments. In either case, 𝕩 is the first argument; 𝕨, if present, is the second. Note that F(x,w) in JS corresponds to w F x in BQN, reversing the visual ordering of the arguments! For modifiers there's no such reversal, as 𝕗 is always the first argument, and for 2-modifiers 𝕘 is the second argument. As in BQN, a modifier may or may not return a function.

Operations may have some extra properties set that aren't terribly important for the JS programmer: for each primitive p, p.glyph gives its glyph, and for a compound operation o such as a train, or a modifier with bound operands, o.repr() decomposes it into its parts. It wouldn't make sense to define either of these properties for a function created in JS.

diff --git a/docs/doc/fill.html b/docs/doc/fill.html new file mode 100644 index 00000000..5ef9baf1 --- /dev/null +++ b/docs/doc/fill.html @@ -0,0 +1,89 @@ + + + + BQN: Fill elements + + +

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 () when a value in 𝕨 is larger than the corresponding length in 𝕩, by the two Nudge functions (»«) when 𝕩 is non-empty, and by First () and Reshape () when 𝕩 is empty. Except for these specific cases, the fill value an array has can't affect the program. The result of Match () 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 () and Nudge (»«) 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  43     # Fill with 0
+⟨ 0 0 0 3 3 3 3 ⟩
+
+    ¯7  "qrst"  # Fill with space
+"   qrst"
+
+

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.

+↗️
    Ȭ 43,"qrst"
+⟨ ⟨ 0 3 3 3 ⟩ " qrs" ⟩
+
+    3⟨⟩  # Fill unknown
+ERROR
+
+    »⟨⟩   # Fill not needed
+⟨⟩
+
+

First () and Reshape () use the fill when 𝕩 is empty, and in the case of Reshape only when the result needs to be non-empty.

+↗️
     ""
+' '
+
+    4 ¨ 0, ""
+⟨ ⟨ 0 0 0 0 ⟩ "    " ⟩
+
+    03  ⟨⟩  # Fill not needed
+↕0‿3
+
+

If for some reason you need to find an array's fill element, the easiest way is 0a.

+↗️
    0"string"
+' '
+
+

How fills are computed

+

For the exact requirements placed on fill, see the specification (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 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" + 432
+" ee"
+
+

Mapping 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 return only numbers and have a fill of 0.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FillMonadsDyadsModifiers
∧∨⥊≍»«⌽⍉⊏⍷⥊↑↓↕⌽⍉/⊏𝔽`
>∾∾≍»«
0≢/⍋⍒∊⊐⊒⍋⍒⊐⊒∊⍷
+

Besides these, there are a few primitives with special fills. Enclose (<) 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 () does the same, although the reason is less obvious: the result elements don't match 𝕩, but they have the same structure.

+

Prefixes and Suffixes (↑↓) use 0𝕩 for the fill, as do Group 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
+⟨ ⟨⟩ ⟨ 0 ⟩ ⟨ 0 1 ⟩ ⟨ 0 1 2 ⟩ ⟨⟩ ⟨⟩ ⟩
+
+    Ȭ 341 / "abc0123A"
+⟨ " ab" " 012" " " ⟩
+
diff --git a/docs/doc/glossary.html b/docs/doc/glossary.html index f1c8d39e..99e96d32 100644 --- a/docs/doc/glossary.html +++ b/docs/doc/glossary.html @@ -119,7 +119,7 @@
  • Nothing: A special value-like entity that comes from ·, 𝕨 in a function with no left argument, or a function called on nothing.
  • Statement: An expression, or nothing (·).
  • Ligature: The character .
  • -
  • List notation: The angle brackets ⟨⟩ or ligatures used to indicate a list.
  • +
  • List notation: The angle brackets ⟨⟩ or ligatures used to indicate a list.
  • Assignment and scoping

    -

    Match compares arrays based on their fundamental properties—shape and elements—and not the fill element, 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 and elements—and not the fill element, 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 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.

    Atomic equality

    Atoms in BQN have six possible types: number, character, function, 1-modifier, 2-modifier, and namespace. Equality is not allowed to fail for any two arguments, so it needs to be defined on all of these types.

    diff --git a/docs/doc/order.html b/docs/doc/order.html index 24db519a..3b281ee3 100644 --- a/docs/doc/order.html +++ b/docs/doc/order.html @@ -21,7 +21,7 @@ "δαβγ" "δγβα"
    -

    Sort Down always matches Sort Up reversed, . The reason for this is that BQN's array ordering is a 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 Sort Up reversed, . The reason for this is that BQN's array ordering is a 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,"").

    Grade

    See the APL Wiki page for a few more examples. BQN only has the monadic form.

    Grade is more abstract than Sort. Rather than rearranging the argument's cells immediately, it returns a list of indices (more precisely, a permutation) giving the ordering that would sort them.

    diff --git a/docs/doc/pick.html b/docs/doc/pick.html index 0926b3a0..b0e99195 100644 --- a/docs/doc/pick.html +++ b/docs/doc/pick.html @@ -6,7 +6,7 @@

    Pick

    Pick () chooses elements from 𝕩 based on index 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 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 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 (), and probably slower. Consider rearranging your data so that you can select along axes instead of picking out elements.

    One element

    When the left argument is a number, Pick gets an element from a list:

    @@ -55,7 +55,7 @@ 4251 ⟨ 0 0 0 0 ⟩ -

    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 for 𝕩.

    ↗️
         ""
     ' '
          π
    diff --git a/docs/doc/reshape.html b/docs/doc/reshape.html
    index 73c85f83..5736da15 100644
    --- a/docs/doc/reshape.html
    +++ b/docs/doc/reshape.html
    @@ -142,7 +142,7 @@
       235 236 237  
                   ┘
     
    -

    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 is used instead, but it's probably best not to invoke this case!

    ↗️
        15  a
     ⟨ 135 136 137 145 146 147 235 236 237 245 246 247 135 136 137 ⟩
     
    @@ -176,7 +176,7 @@
     
  • 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.

    Here's an example. If we try to turn five elements into two rows, gives an error, drops the last element, uses the first element again, and uses a fill element (like 5"abcde" would).

    diff --git a/docs/doc/shift.html b/docs/doc/shift.html index c4b142c6..a85fce92 100644 --- a/docs/doc/shift.html +++ b/docs/doc/shift.html @@ -12,7 +12,7 @@ "end" « "add to the " # Shift After " to the end"
    -

    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 for 𝕩 is used. This kind of shift, which moves cells in 𝕩 over by just one, is called a "nudge".

    ↗️
        » "abcd"   # Nudge
     " abc"
         « 123    # Nudge Back
    diff --git a/docs/doc/take.html b/docs/doc/take.html
    index bf54a147..2126479d 100644
    --- a/docs/doc/take.html
    +++ b/docs/doc/take.html
    @@ -37,7 +37,7 @@
     
    • 𝕩 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 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.
    @@ -56,7 +56,7 @@ 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 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
     ⟨ 0 1 2 3 4 5 ⟩
     
    diff --git a/docs/doc/types.html b/docs/doc/types.html
    index 297989c1..cef41290 100644
    --- a/docs/doc/types.html
    +++ b/docs/doc/types.html
    @@ -43,7 +43,7 @@
     
     
     

    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 () 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), 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

    Data types—numbers, characters, and arrays—are more like "things" than "actions". If called as a function, a value of one of these types simply returns itself. Data can be uniquely represented, compared for equality, and ordered using BQN's array ordering; in contrast, determining whether two functions always return the same result can be undecidable. For arrays, these properties apply only if there are no operations inside. We might say that "data" in BQN refers to numbers, characters, and arrays of data.

    Numbers

    -- cgit v1.2.3