diff options
37 files changed, 478 insertions, 478 deletions
@@ -34,7 +34,7 @@ For longer samples, you can [gaze into the abyss](c.bqn) that is the (incomplete ## Array model -Most of BQN's functionality deals with the manipulation of multidimensional arrays. However, it discards many of the complications of traditional APL [array models](https://aplwiki.com/wiki/Array_model). Unlike in APL, non-array data is possible, and common: numbers, characters, and functions are not arrays (see the full list of [types](#data-types) below). This avoids some difficulties that show up when trying to treat scalar arrays as the fundamental unit; in particular, there is no "floating" so a value is always different from a scalar array that contains it. This system has been [proposed](https://dl.acm.org/doi/abs/10.1145/586656.586663) in APL's past under the name **based array theory**. +Most of BQN's functionality deals with the manipulation of multidimensional arrays. However, it discards many of the complications of traditional APL [array models](https://aplwiki.com/wiki/Array_model). Unlike in APL, non-array data is possible, and common: numbers, characters, and functions are not arrays (see the full list of [types](#types) below). This avoids some difficulties that show up when trying to treat scalar arrays as the fundamental unit; in particular, there is no "floating" so a value is always different from a scalar array that contains it. This system has been [proposed](https://dl.acm.org/doi/abs/10.1145/586656.586663) in APL's past under the name **based array theory**. Currently, the intention is that arrays will not have prototypes, so that all empty arrays of the same shape behave identically. Different elements of an array should not influence each other. While some APLs force numbers placed in the same array to a common representation, which may have different precision properties, BQN will enforce 64-bit floating-point precision, and only use representations or methods compatible with it (for example, integers up to 32 bits). @@ -45,14 +45,14 @@ BQN syntax consists of expressions where computation is done with a little organ ### Expressions Like APL, BQN uses four *syntactic roles* for values in expressions: -* **Values**, like APL arrays or J nouns +* **Subjects**, like APL arrays or J nouns * **Functions**, or verbs in J -* **Modifiers**, like APL monadic operators or J adverbs -* **Compositions**, like APL dyadic operators or J conjunctions. +* **1-Modifiers**, like APL monadic operators or J adverbs +* **2-Modifiers**, like APL dyadic operators or J conjunctions. -These roles work exactly like they do in APL, with functions applying to one or two arguments, modifiers taking a single function or value on the left, and compositions taking a function or value on each side. +These roles work exactly like they do in APL, with functions applying to one or two subject arguments, 1-modifiers taking a single function or subject on the left, and 2-modifiers taking a function or subject on each side. -Unlike APL, in BQN the syntactic role of a value is determined purely by the way it's spelled: a lowercase first letter (`name`) makes it a value, an uppercase first letter (`Name`) makes it a function, and underscores are used for modifiers (`_name`) and compositions (`_name_`). Below, the function `{ππ©}` treats its left argument `π` as a function and its right argument `π©` as an argument. With a list of functions, we can make a table of the square and square root of a few numbers: +Unlike APL, in BQN the syntactic role of a value is determined purely by the way it's spelled: a lowercase first letter (`name`) makes it a subject, an uppercase first letter (`Name`) makes it a function, and underscores are used for 1-modifiers (`_name`) and 2-modifiers (`_name_`). Below, the function `{ππ©}` treats its left argument `π` as a function and its right argument `π©` as a subject. With a list of functions, we can make a table of the square and square root of a few numbers: β¨ΓΛ,ββ© {ππ©}β 1βΏ4βΏ9 β @@ -60,7 +60,7 @@ Unlike APL, in BQN the syntactic role of a value is determined purely by the way 1 2 3 β -BQN's built-in operations also have patterns to indicate the syntactic role: modifiers (`` ΛΒ¨ΛβΌβΒ΄` ``) are all superscript characters, and compositions (`βββΈββΎββΆβββ`) all have an unbroken circle (two functions `β½β` have broken circles with lines through them). Every other built-in constant is a function, although the special symbols `Β―`, `β`, and `Ο` are used as part of numeric literal notation. +BQN's built-in operations also have patterns to indicate the syntactic role: 1-modifiers (`` ΛΒ¨ΛβΌβΒ΄` ``) are all superscript characters, and 2-modifiers (`βββΈββΎββΆβββ`) all have an unbroken circle (two functions `β½β` have broken circles with lines through them). Every other built-in constant is a function, although the special symbols `Β―`, `β`, and `Ο` are used as part of numeric literal notation. ### Special syntax @@ -69,17 +69,17 @@ Most of these glyphs are explained further in the section on [literal notation]( Glyph(s) | Meaning ----------------|----------- `β` | Define -`β©` | Modify +`β©` | Change `β` | Return `β,` or newline | Statement or element separator `()` | Expression grouping -`{}` | Explicit function, modifier, or composition -`β¨β©` | List/vector -`βΏ` | Strand (lightweight vector syntax) +`{}` | Explicit function or modifier +`β¨β©` | List (rank-1 array) +`βΏ` | Strand (lightweight list syntax) `π¨π` | Left argument `π©π` | Right argument -`ππ½` | Left operand (modifier or composition) -`ππΎ` | Right operand (composition) +`ππ½` | Left operand of a modifier +`ππΎ` | Right operand of a 2-modifier `#` | Comment ## Built-in operations @@ -103,7 +103,7 @@ Functions that have significant differences from APL functions are marked with a | `Β¬` | [Not](doc/logic.md)* | [Span](doc/logic.md)* | `\|` | [Absolute Value](https://aplwiki.com/wiki/Magnitude)| [Modulus](https://aplwiki.com/wiki/Residue) | `β€` | | [Less Than or Equal to](https://aplwiki.com/wiki/Less_than_or_Equal_to) -| `<` | [Box](https://aplwiki.com/wiki/Enclose) | [Less Than](https://aplwiki.com/wiki/Less_than) +| `<` | [Enclose](https://aplwiki.com/wiki/Enclose) | [Less Than](https://aplwiki.com/wiki/Less_than) | `>` | [Merge](https://aplwiki.com/wiki/Mix) | [Greater Than](https://aplwiki.com/wiki/Greater_than) | `β₯` | | [Greater Than or Equal to](https://aplwiki.com/wiki/Greater_than_or_Equal_to) | `=` | | [Equals](https://aplwiki.com/wiki/Equal_to) @@ -131,7 +131,7 @@ Functions that have significant differences from APL functions are marked with a | `β·` | [Deduplicate](https://aplwiki.com/wiki/Unique) | [Find](https://aplwiki.com/wiki/Find) | `β` | [Group Indices](doc/group.md)* | [Group](doc/group.md)* -### Modifiers and compositions +### Modifiers *Combinators* only control the application of functions. Because a non-function operand applies as a constant function, some combinators have extra meanings when passed a constant. For example, `0Λ` is the constant function that always returns 0 and `0βΈ<` is the function that tests whether its right argument is greater than 0. @@ -148,16 +148,16 @@ Glyph | Name(s) | Definition | Description Choose isn't really a combinator since it calls the function `β`, and Under is not a true combinator since it has an "undo" step at the end. This step might be implemented using the left operand's inverse (*computational* Under) or its structural properties (*structural* Under). -Other modifiers and compositions control array traversal and iteration. In three cases a simpler modifier is paired with a generalized composition: in each case the modifier happens to be the same as the composition with a right operand of `Β―1`. +Other modifiers control array traversal and iteration. In three cases a simpler 1-modifier is paired with a generalized 2-modifier: in each case the 1-modifier happens to be the same as the 2-modifier with a right operand of `Β―1`. -Modifier | Name | Compositon | Name ----------|---------|------------|-------- -`Λ` | Cells | `β` | Rank -`Β¨` | Each | `β` | Depth -`β` | Table | -`βΌ` | Undo | `β` | Repeat -`Β΄` | Reduce | -`` ` `` | Scan | +1-Modifier | Name | 2-Modifier | Name +-----------|---------|------------|-------- +`Λ` | Cells | `β` | Rank +`Β¨` | Each | `β` | Depth +`β` | Table | +`βΌ` | Undo | `β` | Repeat +`Β΄` | Reduce | +`` ` `` | Scan | ## Literal notation @@ -167,7 +167,7 @@ BQN has single-token notation for numbers, strings, and characters. Numbers allow the typical decimal notation with `Β―` for the negative sign (because `-` is a function) and `e` for scientific notation (or `E`, as numeric notation is case-insensitive). `β` and `Ο` may be used as special numeric values. Complex numbers are also allowed, with the components separated by `i`. -Strings are written with double quotes `""`, and characters with single quotes `''` with a single character in between. A double quote within a string can be escaped by writing it twice. If two string or two character literals are next to each other, they must be separated by a space. +Strings are written with double quotes `""`, and characters with single quotes `''` with a single character in between. A double quote within a string can be escaped by writing it twice; if two string literals are next to each other, they must be separated by a space. In contrast, character literals do not use escapes, as the length is already known. ### Separators @@ -179,15 +179,15 @@ Lists (1-dimensional arrays) are enclosed in angle brackets `β¨β©`, with the r If added, [sets and dictionaries](#sets-and-dictionaries) would also use a list-like notation. -### Explicit functions +### Blocks -Functions, modifiers, and combinators can be defined using curly braces `{}`. The contents are simply a sequence of expressions, where each is evaluated and the result of the last is returned. This result can have any value, and its syntactic role in the calling context is determined by the normal rules: functions return values and modifiers and compositions return functions. Operations defined in this way have lexical scope. +Blocks are written with curly braces `{}` and can be used to group expressions or define functions and modifiers. The contents are simply a sequence of expressions, where each is evaluated and the result of the last is returned in order to evaluate the block. This result can have any value, and its syntactic role in the calling context is determined by the normal rules: functions return subjects and modifiers return functions. Blocks have lexical scope. -The special values `π¨` and `π©`, which stand for arguments, and `π` and `π`, which stand for operands, are available inside curly braces. Like ordinary names, the lowercase forms indicate values and the uppercase forms `πππ½πΎ` indicate functions. The type (including syntactic role) of the result is determined by its contents: a composition contains `π`, a modifier contains `π` but not `π`, and a function contains neither. +The special names `π¨` and `π©`, which stand for arguments, and `π` and `π`, which stand for operands, are available inside curly braces. Like ordinary names, the lowercase forms indicate subjects and the uppercase forms `πππ½πΎ` indicate functions. The type and syntactic role of the block is determined by its contents: a 2-modifier contains `π`, a 1-modifier contains `π` but not `π`, and a function contains neither but does have one of `π¨π©π€πππ`. If no special names are present the block is an *immediate block* and is evaluated as soon as it appears, with the result having a subject role. -A modifier or composition can be evaluated twice: once when passed operands and again when the resulting function is passed arguments. If it contains `π¨` or `π©`, the first evaluation simply remembers the operands, and the contents will be executed only on the second evaluation, when the arguments are available. If it doesn't contain these, then the contents are executed on the first evaluation and the result is treated as a function. +A modifier can be evaluated twice: once when passed operands and again when the resulting function is passed arguments. If it contains `π¨` or `π©`, the first evaluation simply remembers the operands, and the contents will be executed only on the second evaluation, when the arguments are available. If it doesn't contain these, then the contents are executed on the first evaluation and the result is treated as a function. -## Data types +## Types BQN will initially support the following fundamental types: @@ -195,8 +195,8 @@ BQN will initially support the following fundamental types: - Character (Unicode code point) - Array - Function -- Modifier -- Composition +- 1-Modifier +- 2-Modifier All of these types are immutable, and immutable types should be the default for BQN. The only mutable type likely to be added is the namespace or scope. diff --git a/doc/context.md b/doc/context.md index fcd51bde..e3328fc8 100644 --- a/doc/context.md +++ b/doc/context.md @@ -13,17 +13,17 @@ In each case, some values are used as inputs to functions while others are the f a B C _d e -Here, the lowercase spelling indicates that `a` and `e` are to be treated as values ("arrays" in APL) while the uppercase spelling of variables `B` and `C` are used as functions and `_d` is a modifier ("monadic operator"). Like parentheses for function application, the spelling is not inherent to the variable values used, but instead indicates their grammatical role in this particular expression. A variable has no inherent spelling and can be used in any role, so the names `a`, `A`, `_a`, and `_a_` all refer to exact same variable, but in different roles; typically we use the lowercase name to refer to the variable in isolation. While we still don't know anything about what values `a`, `b`, `c`, and so on have, we know how they interact in the line of code above. +Here, the lowercase spelling indicates that `a` and `e` are to be treated as subjects ("arrays" in APL) while the uppercase spelling of variables `B` and `C` are used as functions and `_d` is a 1-modifier ("monadic operator"). Like parentheses for function application, the spelling is not inherent to the variable values used, but instead indicates their grammatical role in this particular expression. A variable has no inherent spelling and can be used in any role, so the names `a`, `A`, `_a`, and `_a_` all refer to exact same variable, but in different roles; typically we use the lowercase name to refer to the variable in isolationβall values are nouns when speaking about them in English. While we still don't know anything about what values `a`, `b`, `c`, and so on have, we know how they interact in the line of code above. ## Is grammatical context really a problem? Yes, in the sense of [problems with BQN](../problems.md). A grammar that uses context is harder for humans to read and machines to execute. A particular difficulty is that parts of an expression you don't yet understand can interfere with parts you do, making it difficult to work through an unknown codebase. -One difficulty beginners to APL will encounter is that code in APL at first appears like a string of undifferentiated symbols. For example, a tacit Unique Mask implementation `β³β¨=β³ββ’` consists of six largely unfamiliar characters with little to distinguish them (in fact, the one obvious bit of structure, the repeated `β³`, is misleading as it means different things in each case!). Simply placing parentheses into the expression, like `(β³β¨)=(β³ββ’)`, can be a great help to a beginner, and part of learning APL is to naturally see where the parentheses should go. The equivalent BQN expression, `βΛ=βββ `, will likely appear equally intimidating at first, but the path to learning which things apply to which is much shorter: rather than learning the entire list of APL primitives, a beginner just needs to know that superscript characters like `Λ` are modifiers and characters like `β` with unbroken circles are compositions before beginning to learn the BQN grammar that will explain how to tie the various parts together. +One difficulty beginners to APL will encounter is that code in APL at first appears like a string of undifferentiated symbols. For example, a tacit Unique Mask implementation `β³β¨=β³ββ’` consists of six largely unfamiliar characters with little to distinguish them (in fact, the one obvious bit of structure, the repeated `β³`, is misleading as it means different things in each case!). Simply placing parentheses into the expression, like `(β³β¨)=(β³ββ’)`, can be a great help to a beginner, and part of learning APL is to naturally see where the parentheses should go. The equivalent BQN expression, `βΛ=βββ `, will likely appear equally intimidating at first, but the path to learning which things apply to which is much shorter: rather than learning the entire list of APL primitives, a beginner just needs to know that superscript characters like `Λ` are 1-modifiers and characters like `β` with unbroken circles are 2-modifiers before beginning to learn the BQN grammar that will explain how to tie the various parts together. This sounds like a distant concern to a master of APL or a computer that has no difficulty memorizing a few dozen glyphs. Quite the opposite: the same concern applies to variables whenever you begin work with an unfamiliar codebase! Many APL programmers even enforce variable name conventions to ensure they know the class of a variable. By having such a system built in, BQN keeps you from having to rely on programmers following a style guide, and also allows greater flexibility, including [functional programming](functional.md), as we'll see later. -Shouldn't a codebase define all the variables it uses, so we can see their class from the definition? Not always: consider that in a language with libraries, code might be imported from dependencies. Many APLs also have some dynamic features that can allow a variable to have more than one class, such as the `βΊββ’` pattern in a dfn that makes `βΊ` an array in the dyadic case but a function in the monadic case. Regardless, searching for a definition somewhere in the code is certainly a lot more work than knowing the class right away! One final difficulty is that even one unknown can delay understanding of an entire expression. Suppose in `A B c`, `B` is a function and `c` is an array, and both values are known to be constant. If `A` is known to be a function (even if its value is not yet known), its right argument `B c` can be evaluated ahead of time. But if `A`'s type isn't known, it's impossible to know if this optimization is worth it, because if it is an array, `B` will instead be called dyadically. +Shouldn't a codebase define all the variables it uses, so we can see their class from the definition? Not always: consider that in a language with libraries, code might be imported from dependencies. Many APLs also have some dynamic features that can allow a variable to have more than one class, such as the `βΊββ’` pattern in a dfn that makes `βΊ` an array in the dyadic case but a function in the monadic case. Regardless, searching for a definition somewhere in the code is certainly a lot more work than knowing the class just from looking! One final difficulty is that even one unknown can delay understanding of an entire expression. Suppose in `A B c`, `B` is a function and `c` is an array, and both values are known to be constant. If `A` is known to be a function (even if its value is not yet known), its right argument `B c` can be evaluated ahead of time. But if `A`'s type isn't known, it's impossible to know if this optimization is worth it, because if it is an array, `B` will instead be called dyadically. ## BQN's spelling system @@ -31,52 +31,52 @@ BQN's expression grammar is a simplified version of the typical APL, removing so | BQN | APL | J |-------------|------------------|------ -| Value | Array | Noun +| Subject | Array | Noun | Function | Function | Verb -| Modifier | Monadic operator | Adverb -| Composition | Dyadic operator | Conjunction +| 1-modifier | Monadic operator | Adverb +| 2-modifier | Dyadic operator | Conjunction -Unlike variables, BQN primitives have only one spelling, and a fixed role (but their values can be used in a different role by storing them in variables). Superscript glyphs `` ΛΒ¨ΛβΌβΒ΄` `` are used for modifiers, and glyphs `βββΈββΎββΆβββ` with an unbroken circle are compositions. Other primitives are functions. String and numeric literals are values. +Unlike variables, BQN primitives have only one spelling, and a fixed role (but their values can be used in a different role by storing them in variables). Superscript glyphs `` ΛΒ¨ΛβΌβΒ΄` `` are used for 1-modifiers, and glyphs `βββΈββΎββΆβββ` with an unbroken circle are 2-modifiers. Other primitives are functions. String and numeric literals are subjects. -BQN's variables use another system, where the spelling indicates how the variable's value is used. A variable spelled with a lowercase first letter, like `var`, is a value. Spelled with an uppercase first letter, like `Var`, it is a function. Underscores are placed where operands apply to indicate a modifier `_var` or composition `_var_`. Other than the first letter or underscore, variables are case-insensitive. +BQN's variables use another system, where the spelling indicates how the variable's value is used. A variable spelled with a lowercase first letter, like `var`, is a subject. Spelled with an uppercase first letter, like `Var`, it is a function. Underscores are placed where operands apply to indicate a 1-modifier `_var` or 2-modifier `_var_`. Other than the first letter or underscore, variables are case-insensitive. The associations between spelling and syntactic role are considered part of BQN's [token formation rules](../spec/token.md). -One rule for typing is also best considered to be a pre-parsing rule like the spelling system: the role of a brace construct `{}` with no header is determined by which special arguments it uses: it's a value if there are none, but a `π¨` or `π©` makes it at least a function, an `π½` makes it a modifier or composition, and a `πΎ` always makes it a composition. +One rule for typing is also best considered to be a pre-parsing rule like the spelling system: the role of a brace construct `{}` with no header is determined by which special arguments it uses: it's a subject if there are none, but a `π¨` or `π©` makes it at least a function, an `π½` makes it a 1- or 2-modifier, and a `πΎ` always makes it a 2-modifier. ## BQN's grammar A formal treatment is included in [the spec](../spec/grammar.md). BQN's grammarβthe ways syntactic roles interactβfollows the original APL model (plus trains) closely, with allowances for new features like list notation. In order to keep BQN's syntax context-free, the syntactic role of any expression must be known from its contents, just like tokens. -Here is a table of the APL-derived operator and function application rules: +Here is a table of the APL-derived modifier and function application rules: -| left | main | right | output | name -|-------|-------|-------|----------|------ -| | `F` | `x` | Value | Monadic function -| `w` | `F` | `x` | Value | Dyadic function -| | `F` | `G` | Function | 2-train -| `F*` | `G` | `H` | Function | 3-train -| `F*` | `_m` | | Function | Modifier -| `F*` | `_c_` | `G*` | Function | Composition -| | `_c_` | `G*` | Modifier | Partial application -| `F*` | `_c_` | | Modifier | Partial application +| left | main | right | output | name +|-------|-------|-------|------------|------ +| | `F` | `x` | Subject | Monadic function +| `w` | `F` | `x` | Subject | Dyadic function +| | `F` | `G` | Function | 2-train +| `F*` | `G` | `H` | Function | 3-train +| `F*` | `_m` | | Function | 1-Modifier +| `F*` | `_c_` | `G*` | Function | 2-Modifier +| | `_c_` | `G*` | 1-Modifier | Partial application +| `F*` | `_c_` | | 1-Modifier | Partial application -A function with an asterisk indicates that a value can also be used: in these positions there is no difference between function and value spellings. Operator applications bind more tightly than functions, and associate left-to-right while functions associate right-to-left. +A function with an asterisk indicates that a subject can also be used: in these positions there is no difference between function and subject spellings. Modifier applications bind more tightly than functions, and associate left-to-right while functions associate right-to-left. -BQN lists can be written with angle brackets `β¨elt0,elt1,β¦β©` or ligatures `elt0βΏelt1βΏβ¦`. In either case the elements can have any type, and the result is a value. +BQN lists can be written with angle brackets `β¨elt0,elt1,β¦β©` or ligatures `elt0βΏelt1βΏβ¦`. In either case the elements can have any type, and the result is a subject. -The statements in a brace block, function, or operator can also be any role, including the return value at the end. These roles have no effect: outside of braces, a function always returns an array, a modifier always returns a function, and so on, regardless of how these objects were defined. +The statements in a block can also be any role, including the return value at the end. These roles have no effect: outside of braces, an immediate block is a subject, a function always returns a subject, and a modifier always returns a function, regardless of how these objects were defined. ## Mixing roles -BQN's basic types align closely with its syntactic roles: functions, modifiers, and compositions are all basic types, while values are split into numbers, characters, and arrays. This is no accident, and usually values will be used in roles that match their underlying type. However, the ability to use a role that doesn't match the type is very useful. +BQN's value types align closely with its syntactic roles: functions, 1-modifiers, and 2-modifiers are all types (*operation* types) as well as roles, while the other types (*data* types) are split into numbers, characters, and arrays. This is no accident, and usually values will be used in roles that correspond to their underlying type. However, the ability to use a role that doesn't match the type is also useful. -Any type can be passed as an argument to a function, or as an operand, by treating it as a value. This means that BQN fully supports Lisp-style [functional programming](functional.md), where functions can be used as values. +Any type can be passed as an argument to a function, or as an operand, by treating it as a subject. This means that BQN fully supports Lisp-style [functional programming](functional.md), where functions can be used as first-class entities. -It can also be useful to treat a value type as a function, in which case it applies as a constant function. This rule is useful with most built-in operators. For example, `Fβ1` uses a constant for the rank even though in general a function can be given, and `aβΎ(bβΈ/)` inserts the values in `a` into the positions selected by `b`, ignoring the old values rather than applying a function to them. +It can also be useful to treat a value of a data type as a function, in which case it applies as a constant function. This rule is useful with most built-in modifiers. For example, `Fβ1` uses a constant for the rank even though in general a function can be given, and if `a` is an array then `aβΎ(bβΈ/)` inserts the values in `a` into the positions selected by `b`, ignoring the old values rather than applying a function to them. -Other mixes of roles are generally not useful. While a combination such as treating a function as a modifier is allowed, attempting to apply it to an operand will fail. Only a modifier can be applied as a modifier and only a composition can be applied as a composition. Only a function or value can be applied as a function. +Other mixes of roles are generally not useful. While a combination such as treating a function as a modifier is allowed, attempting to apply it to an operand will fail. Only a 1-modifier can be applied as a 1-modifier and only a 2-modifier can be applied as a 2-modifier. Only a function or data can be applied as a function. -It's also worth noting that something that appears to be a value may actually be a function! For example, the result of `π¨Λπ©` may not always be `π¨`. `π¨Λπ©` is exactly identical to `πΛπ©`, which gives `π©ππ©`. If `π` is a number, character, or array, that's the same as `π¨`, but if it is a function, then it will be applied. +It's also worth noting that a subject may unexpectedly be a function! For example, the result of `π¨Λπ©` may not always be `π¨`. `π¨Λπ©` is exactly identical to `πΛπ©`, which gives `π©ππ©`. If `π` is a number, character, or array, that's the same as `π¨`, but if it is a function, then it will be applied. -The primary way to change the role of a value in BQN is to use a name, including one of the arguments to a brace function. In particular, you can use `{π½}` to convert a value operand into a function. Converting a function to a value is more difficult. Often an array of functions is wanted, in which case they can be stranded together; otherwise it's probably best to give the function a name. Picking a function out of a list, for example `ββ¨+β©` will give it as a value. +The primary way to change the role of a value in BQN is to use a name, including one of the special names for inputs to a brace function or modifier. In particular, you can use `{π½}` to convert a subject operand into a function. Converting a function to a subject is more difficult. Often an array of functions is wanted, in which case they can be stranded together; otherwise it's probably best to give the function a name. Picking a function out of a list, for example `ββ¨+β©`, will give it as a subject. diff --git a/doc/depth.md b/doc/depth.md index 2bb369ef..a901982c 100644 --- a/doc/depth.md +++ b/doc/depth.md @@ -1,6 +1,6 @@ # Depth -The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (`β‘`) returns the depth of its argument, while the composition Depth (`β`) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes. +The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (`β‘`) returns the depth of its argument, while the 2-modifier Depth (`β`) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes. ## The Depth function @@ -27,7 +27,7 @@ Also unlike rank, Depth *does* care about the elements of its argument: in fact, β‘ β¨2,<3,4,<<<5β© 4 -As the above expressions suggest, the depth of an array is the maximum of its elements, plus one. The base case, a non-array (including a function, modifier, or combinator), has depth 0. +As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, a non-array (including a function or modifier), has depth 0. β‘'c' 0 @@ -38,7 +38,7 @@ As the above expressions suggest, the depth of an array is the maximum of its el β‘β¨5,β¨'c',f,2β©β© 2 -If the function `IsArray` indicates whether its argument is an array, then we can write a recursive definition of Depth using the Choose composition. +If the function `IsArray` indicates whether its argument is an array, then we can write a recursive definition of Depth using the Choose modifier. DepthβIsArrayβΆ0βΏ{1+0βΒ΄DepthΒ¨β₯π©} @@ -73,7 +73,7 @@ In these cases the flexibility seems trivial because the left argument has depth [ 2 1 ] [ 2 4 ] [ 2 1 ] β -This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a vector, and convert any non-array argument into an array by boxing it: +This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a list, and convert any non-array argument into an array by enclosing it: β¨3βΏ2,1β© <β(0=β‘)Β¨βΈβ β6βΏ7 [ [ 3 1 ] [ 2 1 ] ] @@ -90,9 +90,9 @@ For Select, the depth-1 case is still quite useful, but it may also be desirable 2βΏ1βΏ4 <Β¨βΈβ β3βΏ4βΏ5βΏ2 [ [ 2 1 4 0 ] [ 2 1 4 1 ] ] -## The Depth composition +## The Depth modifier -The Depth composition (`β`) is a generalization of Each that allows diving deeper into an array. To illustrate it we'll use a shape `4βΏ3` array of lists of lists. +The Depth 2-modifier (`β`) is a generalization of Each that allows diving deeper into an array. To illustrate it we'll use a shape `4βΏ3` array of lists of lists. β’ n β <β1β2 4βΏ3βΏ2βΏ2β₯β48 β diff --git a/doc/fromDyalog.md b/doc/fromDyalog.md index bbb99376..ced9c774 100644 --- a/doc/fromDyalog.md +++ b/doc/fromDyalog.md @@ -16,7 +16,7 @@ Here are some closest equivalents in Dyalog APL for the BQN functions that don't | Monad | `,β` | `β½,β½ββ½` | `β³` | `βΈ` | `β` | `β` | `β£βΏ` | `β` | | `β¦` | `β ` | `βͺ` | `βΈ` | | Dyad | `β` | `β` | `,βΏ` | `βΏ` | `βΈ` | `β½βΈβ½` | `β·` | `β` | `β³` | `β¦` | `β` | `β·` | `βΈ` or `β` | -Modifiers and combinators are a little harder. Many have equivalents in some cases, but Dyalog sometimes chooses different functionality based on whether the operand is an array. In BQN an array is always treated as a constant function. +Modifiers are a little harder. Many have equivalents in some cases, but Dyalog sometimes chooses different functionality based on whether the operand is an array. In BQN an array is always treated as a constant function. | BQN | `Β¨` | `β` | `Β΄` | `β` | `β` | `Λ` | `β` | `β` | `β` | |--------|-----|------|-----|-----|-----|-----|-----|-----|-----| diff --git a/doc/functional.md b/doc/functional.md index edc20c5f..45614904 100644 --- a/doc/functional.md +++ b/doc/functional.md @@ -4,7 +4,7 @@ BQN boasts of its functional capabilities, including first-class functions. What First, let's be clear about what the terms we're using mean. A language has *first-class functions* when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments. -Traditionally APL has worked around its lack of first-class functions with operators or second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class. +Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class. The term *functional programming* is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called *first-class functional programming*, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, two other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term *function-level programming* for this usage. A newer usage, which I call *pure functional programming*, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, *typed functional programming* is closely associated with pure functional programming and refers to statically-typed functional languages such as Haskell, F#, and Idris (the last of which even supports *dependently-typed functional programming*, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it is dynamically and not statically typed. @@ -26,26 +26,26 @@ What does functional programming in BQN look like? How is it different from the ### Working with roles -First, let's look at the basics: a small program that takes a function as its argument and result. The function `Lin` below gives a linear approximation to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, `π`. +First, let's look at the basics: a small program that has functions as its argument and result. The function `Lin` below gives a linear approximation to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, `π`. Lin β { v0 β π 0 v0 + ((π 1) - v0) Γ β’ } -We can pass it the exponential function as an argument by giving it the name `Exp` and then referring to it in lowercase (that is, in a value role). The result is a train that adds 1 to *e*-1 times the argument. +We can pass it the exponential function as an argument by giving it the name `Exp` and then referring to it in lowercase (that is, in a subject role). The result is a train that adds 1 to *e*-1 times the argument. Exp β β Lin exp (1 + (1.71828182845905 Γ β’)) -As with all functions, the result of `Lin` has a value role. To use it as a function, we give it a name and then use that name with an uppercase spelling. +As with all functions, the result of `Lin` has a subject role. To use it as a function, we give it a name and then use that name with an uppercase spelling. expLin β Lin exp ExpLin 5 9.59140914229523 -A tricker but more compact method is to use the modifier `{π½}`, as the input to a modifier can have a value or function role but its output always has a function role. +A tricker but more compact method is to use the 1-modifier `{π½}`, as the input to a modifier can have a subject or function role but its output always has a function role. (Lin exp){π½} 5 9.59140914229523 @@ -69,12 +69,12 @@ Its call syntax is simpler as well. In other cases, however, the function versio ### Arrays of functions -It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as values. Here's an example of an array of functions with a reduction applied to it, composing them together. +It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a reduction applied to it, composing them together. {πβπ}Β΄ ββΏ-βΏ(ΓΛ) ββ(-β(ΓΛ)) -Like any function, this one can be given a name and then called. A quirk of this way of defining a function is that it has a value role (it's the result of the function `{πβπ}Β΄`) and so must be defined with a lowercase name. +Like any function, this one can be given a name and then called. A quirk of this way of defining a function is that it has a subject role (it's the result of the function `{πβπ}Β΄`) and so must be defined with a lowercase name. gauss β {πβπ}Β΄ ββΏ-βΏ(ΓΛ) Gauss 2 @@ -85,9 +85,9 @@ Another, and probably more common, use of arrays of functions is to apply severa β¨β, 2βΈβΎ, β’-ββ© {ππ©}Β¨ 9 [ 3 [ 2 9 ] Β―8094.083927575384 ] -The composition Choose (`βΆ`) relies on arrays of functions toβ¦ function. It's very closely related to Pick `β`, and in fact when the left operand and the elements of the right operand are all value types there's no real difference: Choose returns the constant function `πβπ`. +The 2-modifier Choose (`βΆ`) relies on arrays of functions toβ¦ function. It's very closely related to Pick `β`, and in fact when the left operand and the elements of the right operand are all data there's no real difference: Choose returns the constant function `πβπ`. - 2βΆ"abcdef" "arg" + 2βΆ"abcdef"βΏ"arg" c When the operands contain functions, however, the potential of Choose as a ternary-or-more operator opens up. Here's a function for a step in the Collatz sequence, which halves an even input but multiplies an odd input by 3 and adds 1. To get the sequence for a number, we can apply the same function many times. It's an open problem whether the sequence always ends with the repetition 4, 2, 1, but it can take a surprisingly long time to get thereβtry 27 as an argument. diff --git a/doc/group.md b/doc/group.md index b561cd7d..197e8977 100644 --- a/doc/group.md +++ b/doc/group.md @@ -10,7 +10,7 @@ Once defined, the old BQN Key (dyadic) is `β·βΈββΈβ` and Group (monadic) ## Definition -Group operates on a numeric list of indices and a value array, treated as a list of its major cells, to produce a list of groups, each of which is a selection from the values. The indices and values have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the value should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result. +Group operates on a numeric list of indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result. 0βΏ1βΏ2βΏ0βΏ1 β "abcde" # Corresponding indices and values β @@ -20,7 +20,7 @@ Group operates on a numeric list of indices and a value array, treated as a list 0βΏ1βΏ2βΏ0βΏ1 β "abcde" # Values grouped by index [ [ ad ] [ be ] [ c ] ] -For example, we might choose to group a list of words by length. Within each group, values maintain the ordering they had in the list originally. +For example, we might choose to group a list of words by length. Within each group, cells maintain the ordering they had in the list originally. phrase β "BQN"βΏ"uses"βΏ"notation"βΏ"as"βΏ"a"βΏ"tool"βΏ"of"βΏ"thought" β₯Λ β Β¨βΈβ phrase @@ -40,7 +40,7 @@ For example, we might choose to group a list of words by length. Within each gro If we'd like to ignore words of 0 letters, or more than 5, we can set all word lengths greater than 5 to 0, then reduce the lengths by 1. Two words end up with left argument values of Β―1 and are omitted from the result. - 1-Λβ€β5βΈΓβ Β¨phrase + 1 -Λ β€β5βΈΓ β Β¨ phrase [ 2 3 Β―1 1 0 3 1 Β―1 ] β₯Λ {1-Λβ€β5βΈΓβ Β¨π©}βΈβ phrase β @@ -52,7 +52,7 @@ If we'd like to ignore words of 0 letters, or more than 5, we can set all word l Note that the length of the result is determined by the largest index. So the result never includes trailing empty groups. A reader of the above code might expect 5 groups (lengths 1 through 5), but there are no words of length 5, so the last group isn't there. -When Group is called dyadically, the left argument is used for the indices and the right is used for values, as seen above. When it is called monadically, the right argument gives the indices and the values grouped are the right argument's indices, that is, `ββ π©`. +When Group is called dyadically, the left argument is used for the indices and the right is used for values, as seen above. When it is called monadically, the right argument, which must be a list, gives the indices and the values grouped are the right argument's indices, that is, `ββ π©`. β₯Λ β 2βΏ3βΏΒ―1βΏ2 β @@ -66,7 +66,7 @@ Here, the index 2 appears at indices 0 and 3 while the index 3 appears at index ### Multidimensional grouping -Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a vector of numeric vectors, and the result has rank `β π¨` while its elementsβas alwaysβhave the same rank as `π©`. The result shape is `1+β´¨π¨`, while the shape of element `iβπ¨βπ©` is `i+Β΄β=Β¨π¨`. If every element of `π¨` is sorted ascending and contains only non-negative numbers, we have `π©β‘βΎπ¨βπ©`, that is, Join is the inverse of Partition. +Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank `β π¨` while its elementsβas alwaysβhave the same rank as `π©`. The result shape is `1+β´¨π¨`, while the shape of element `iβπ¨βπ©` is `i+Β΄β=Β¨π¨`. If every element of `π¨` is sorted ascending and contains only non-negative numbers, we have `π©β‘βΎπ¨βπ©`, that is, Join is the inverse of Partition. Here we split up a rank-2 array into a rank-2 array of rank-2 arrays. Along the first axis we simply separate the first pair and second pair of rowsβa partition. Along the second axis we separate odd from even indices. @@ -84,7 +84,7 @@ Here we split up a rank-2 array into a rank-2 array of rank-2 arrays. Along the Each group `iβπ¨βπ©` is composed of the cells `j<Β¨βΈβπ©` such that `iβ’jβΒ¨π¨`. The groups retain their array structure and ordering along each argument axis. Using multidimensional Replicate we can say that `iβπ¨βπ©` is `(i=π¨)/π©`. -The monadic case works similarly: Group Indices always satisfies `βπ© ββ π©βββ β1 x`. As with `β`, the depth of the result of Group Indices is always one greater than that of its argument. A depth-0 argument is not allowed. +The monadic case works similarly: Group Indices always satisfies `βπ© ββ π©βββ β1π©`. As with `β`, the depth of the result of Group Indices is always one greater than that of its argument. A depth-0 argument is not allowed. ## Properties diff --git a/doc/indices.md b/doc/indices.md index 382f7f80..0634087d 100644 --- a/doc/indices.md +++ b/doc/indices.md @@ -1,16 +1,16 @@ # Indices -One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the leading axis theory, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0) indices, which can refer to vector elements or array major cells (or more generally indexing along any particular axis). When using this kind of element indices, arrays are required to be vectors. Only two functions allow the use of vector element indices: Range (`β`), which can accept a vector argument, and Pick (`β`), which uses the depth-1 arrays in its left argument as index scalars or vectors. +One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the leading axis theory, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using this kind of element index, indexed arrays are required to be lists. Only two functions allow the use of list element indices: Range (`β`), which can accept a list argument, and Pick (`β`), which uses the depth-1 arrays in its left argument as index scalars or lists. Others use single-number indices to refer to cells. -The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. `β` is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the return type of indices is as useful as possible. +The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. `β` is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the kind of index returned is as useful as possible. | Monad | Dyad | Where | How |-------|------|---------|-------------------------- -| `β` | | | Element scalar or vector +| `β` | | | Element scalar or list | `/` | | | Element scalar | `β` | | | Element scalar | `β` | `β` | `π¨`/`π©` | Along-axis scalar -| | `β` | `π¨` | Element vector +| | `β` | `π¨` | Element list | `β` | `β` | | Major cell scalar | `β` | `β` | | Major cell scalar | | `β` | | Major cell scalar @@ -18,29 +18,29 @@ The following functions take or return indices. Except where marked, the indices | | `β` | `π¨` | Major cell or along-axis scalar | `β` | | | Axis scalar -Dyadic Transpose (`β`) takes an index into the right argument axes as its left argument, but since array shape is 1-dimensional, there is only one sensible choice for this, a single number. +Dyadic Transpose (`β`) uses indices into the right argument axes in its left argument, but since array shape is 1-dimensional, there is only one sensible choice for this, a single number. # Element indices -In general, the index of an element of an array is a vector whose length matches the array rank. It is also possible to use a number for an index into a vector, as the vector index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (`β` in BQN) return a numeric vector when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency. +In general, the index of an element of an array is a list whose length matches the array rank. It is also possible to use a number for an index into a list, as the list index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (`β` in BQN) return a numeric list when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency. -Functions `β`, `/`, `β`, and `β` naturally deal with element indices. Each of these can be defined to use vector indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts `β` and monadic `/` to use depth-0 indices, which comes with the requirement that the arguments to monadic `/` and `β`, and the result of monadic `β`, must be vectors. For dyadic `β` the depth-1 elements of the left argument are vectors of indices along axes of the result; see [the documentation](group.md#multidimensional-grouping). The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on vector indices. +Functions `β`, `/`, `β`, and `β` naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts `β` and monadic `/` to use depth-0 indices, which comes with the requirement that the arguments to monadic `/` and `β`, and the result of monadic `β`, must be lists. For dyadic `β` the depth-1 elements of the left argument are lists of indices along axes of the result; see [the documentation](group.md#multidimensional-grouping). The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on list indices. -Unlike `/` and `β`, `β` and `β` do use vector element indices. For `β` this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric vector); if passed a vector, it uses vector indices and the result has depth 2 (the result depth is always one greater than the argument depth). For `β`, vector indices are chosen because `β` handles scalar indices well already. When selecting multiple elements from a vector, they would typically have to be placed in an array, which is equivalent to `β` with a numeric vector left argument. A single scalar index to `β` is converted to a vector, so it can be used to select a single element if only one is wanted. To select multiple elements, `β` uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element vector an index, or does it contain an index? +Unlike `/` and `β`, `β` and `β` do use list element indices. For `β` this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For `β`, list indices are chosen because `β` handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to `β` with a numeric list left argument. A single scalar index to `β` is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, `β` uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index? # Major cell indices -One of the successes of the [leading axis model](https://aplwiki.com/wiki/Leading_axis_theory) is to introduce a kind of index for multidimensional arrays that is easier to work with than vector indices. The model introduces [cells](https://aplwiki.com/wiki/Cell), where a cell index is a vector of any length up to the containing array's rank. General cell indices are discussed in the next section; first we introduce a special case, indices into major cells or Β―1-cells. These cells naturally form a list, so the index of a major cell is a single number. These indices can also be considered indices along the first axis, since an index along any axis is a single number. +One of the successes of the [leading axis model](https://aplwiki.com/wiki/Leading_axis_theory) is to introduce a kind of index for multidimensional arrays that is easier to work with than list indices. The model introduces [cells](https://aplwiki.com/wiki/Cell), where a cell index is a list of any length up to the containing array's rank. General cell indices are discussed in the next section; first we introduce a special case, indices into major cells or Β―1-cells. These cells naturally form a list, so the index of a major cell is a single number. These indices can also be considered indices along the first axis, since an index along any axis is a single number. Ordering-based functions `β`, `β`, `β`, and `β` only really make sense with major cell indices: while it's possible to order other indices as ravel indices, this probably isn't useful from a programming standpoint. Note that `β` only uses the ordering in an incidental way, because it's defined to return the *first* index where a right argument cell is found. A mathematician would be more interested in a "pre-image" function that returns the set of all indices where a particular value appears. However, programming usefulness and consistency with the other search functions makes searching for the first index a reasonable choice. -Only one other functionβbut an important one!βdeals with cells rather than elements: `β`, cell selection. Like dyadic `ββββ½β` (depth 0) and `/β` (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a vector of depth-1 elements. In each case the depth-1 arrays index into a single axis. +Only one other functionβbut an important one!βdeals with cells rather than elements: `β`, cell selection. Like dyadic `ββββ½β` (depth 0) and `/β` (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis. # General cell indices BQN does not use general cell indices directly, but it is useful to consider how they might work, and how a programmer might implement functions that use them in BQN if needed. The functions `/`, `β`, and `β` are the ones that can work with indices for multidimensional arrays but don't already. Here we will examine how multidimensional versions would work. -A cell index into an array of rank `r` is a numeric vector of length `lβ€r`, which then refers to a cell of rank `r-l`. In BQN, the cell at index `i` of array `a` is `i<Β¨βΈβa`. +A cell index into an array of rank `r` is a numeric list of length `lβ€r`, which then refers to a cell of rank `r-l`. In BQN, the cell at index `i` of array `a` is `i<Β¨βΈβa`. Because the shape of a cell index relates to the shape of the indexed array, it makes sense not to enclose cell indices, instead treating them as rows of an index array. A definition for `β` for depth-1 left arguments of rank at least 1 follows: replace each row of the left argument with the indexed cell of the right, yielding a result with the same depth as the right argument and shape `π¨((Β―1ββ£)βΎ(Β―1ββ£)βΈβ)ββ’π©`. diff --git a/doc/join.md b/doc/join.md index cb940699..31ec3876 100644 --- a/doc/join.md +++ b/doc/join.md @@ -1,6 +1,6 @@ # Join -Join (`βΎ`) is an extension of the monadic function [Raze](https://aplwiki.com/wiki/Raze) from A+ and J to arbitrary argument ranks. It has the same relationship to Join to, the dyadic function sharing the same glyph, as Unbox (`>`) does to Couple (`β`): `aβb` is `>aβΏb` and `aβΎb` is `βΎaβΏb`. While Unbox and Couple combine arrays (the elements of Unbox's argument, or the arguments themselves for Coups) along a new leading axis, Join and Join to combine them along the existing leading axis. Both Unbox and Join can also be called on a higher-rank array, causing Unbox to add multiple leading axes while Join combines elements along multiple existing axes. +Join (`βΎ`) is an extension of the monadic function [Raze](https://aplwiki.com/wiki/Raze) from A+ and J to arbitrary argument ranks. It has the same relationship to Join to, the dyadic function sharing the same glyph, as Merge (`>`) does to Couple (`β`): `aβb` is `>aβΏb` and `aβΎb` is `βΎaβΏb`. While Merge and Couple combine arrays (the elements of Merge's argument, or the arguments themselves for Couple) along a new leading axis, Join and Join to combine them along the existing leading axis. Both Merge and Join can also be called on a higher-rank array, causing Merge to add multiple leading axes while Join combines elements along multiple existing axes. Join can be used to combine several strings into a single string, like `array.join()` in Javascript (but it doesn't force the result to be a string). @@ -38,6 +38,6 @@ However, Join has higher-dimensional uses as well. Given a rank-`m` array of ran 3 3 3 3 4 4 5 5 5 5 5 β -Join has fairly strict requirements on the shapes of its argument elementsβalthough less strict than those of Unbox, which requires they all have identical shape. Suppose the argument to Join has rank `m`. Each of its elements must have the same rank, `n`, which is at least `m`. The trailing shapes `mβββ’Β¨π©` must all be identical (the trailing shape `mββ’βΎπ©` of the result will match these shapes as well). The other entries in the leading shapes need not be the same, but the shape of an element along a particular axis must depend only on the location of the element along that axis in the full array. For a vector argument this imposes no restriction, since the one leading shape element is allowed to depend on position along the only axis. But for higher ranks the structure quickly becomes more rigid. +Join has fairly strict requirements on the shapes of its argument elementsβalthough less strict than those of Merge, which requires they all have identical shape. Suppose the argument to Join has rank `m`. Each of its elements must have the same rank, `n`, which is at least `m`. The trailing shapes `mβββ’Β¨π©` must all be identical (the trailing shape `mββ’βΎπ©` of the result will match these shapes as well). The other entries in the leading shapes need not be the same, but the shape of an element along a particular axis must depend only on the location of the element along that axis in the full array. For a list argument this imposes no restriction, since the one leading shape element is allowed to depend on position along the only axis. But for higher ranks the structure quickly becomes more rigid. -To state this requirement more formally in BQN, we say that there is some vector `s` of length vectors, so that `(β’Β¨s)β‘β’π©`. We require element `iβπ©` to have shape `iβΒ¨s`. Then the first `m` axes of the result are `+´¨s`. +To state this requirement more formally in BQN, we say that there is some list `s` of lists of lengths, so that `(β’Β¨s)β‘β’π©`. We require element `iβπ©` to have shape `iβΒ¨s`. Then the first `m` axes of the result are `+´¨s`. diff --git a/doc/logic.md b/doc/logic.md index 0e38fb1d..638bdace 100644 --- a/doc/logic.md +++ b/doc/logic.md @@ -14,7 +14,7 @@ We define: And β Γ Or β ΓβΎΒ¬ -Note that `Β¬βΌ ββ Β¬`, since the first added 1 will be negated but the second won't; the two 1s cancel leaving two subtractions, and `-βΌ ββ -`. An alternate definition of Or that matches the typical formula from probability theory is +Note that `Β¬βΌ ββ Β¬`, since when applying `Β¬` twice the first added 1 will be negated but the second won't; the two 1s cancel leaving two subtractions, and `-βΌ ββ -`. An alternate definition of Or that matches the typical formula from probability theory is Or β +-Γ diff --git a/doc/transpose.md b/doc/transpose.md index 006f0de6..73653e0e 100644 --- a/doc/transpose.md +++ b/doc/transpose.md @@ -42,7 +42,7 @@ But, reading in ravel order, the argument and result have exactly the same eleme β β -To exchange multiple axes, use the Power operator. Like Rotate, a negative power will move axes in the other direction. In particular, to move the last axis to the front, use Inverse (as you might expect, this exactly inverts `β`). +To exchange multiple axes, use the Power modifier. Like Rotate, a negative power will move axes in the other direction. In particular, to move the last axis to the front, use Inverse (as you might expect, this exactly inverts `β`). β’ ββ3 a23456 [ 5 6 2 3 4 ] @@ -51,7 +51,7 @@ To exchange multiple axes, use the Power operator. Like Rotate, a negative power In fact, we have `β’ββk a ββ kβ½β’a` for any number `k` and array `a`. -To move axes other than the first, use the Rank operator in order to leave initial axes untouched. A rank of `k>0` transposes only the last `k` axes while a rank of `k<0` ignores the first `|k` axes. +To move axes other than the first, use the Rank modifier in order to leave initial axes untouched. A rank of `k>0` transposes only the last `k` axes while a rank of `k<0` ignores the first `|k` axes. β’ ββ3 a23456 [ 2 3 5 6 4 ] @@ -104,7 +104,7 @@ Finally, it's worth noting that, as monadic Transpose moves the first axis to th Here we define the two valences of Transpose more precisely. -A non-array right argument to Transpose is always boxed to get a scalar array before doing anything else. +A non-array right argument to Transpose is always enclosed to get a scalar array before doing anything else. Monadic transpose is identical to `(β ββ’-1Λ)βΈβ`, except that for scalar arguments it returns the array unchanged rather than giving an error. diff --git a/doc/windows.md b/doc/windows.md index d4165d62..82397a42 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -1,6 +1,6 @@ # Windows -In BQN, it's strongly preferred to use functions, and not operators (modifiers and compositions), for array manipulation. Functions are simpler as they have fewer moving parts. They are more concrete, since the array results can always be viewed right away. They are easier to implement with reasonable performance as well, since there is no need to recognize many possible function operands as special cases. +In BQN, it's strongly preferred to use functions, and not modifiers, for array manipulation. Functions are simpler as they have fewer moving parts. They are more concrete, since the array results can always be viewed right away. They are easier to implement with reasonable performance as well, since there is no need to recognize many possible function operands as special cases. The Window function replaces APL's Windowed Reduction, J's more general Infix operator, and Dyalog's Stencil, which is adapted from one case of J's Cut operator. @@ -24,11 +24,11 @@ You can take a slice of an array `π©` that has length `l` and starts at index 5β2β"abcdefg" [ cdefg ] -Windows differs from Prefixes and Suffixes in that it doesn't add a layer of nesting (it doesn't box each slice). This is possible because the slices have a fixed size. +Windows differs from Prefixes and Suffixes in that it doesn't add a layer of nesting (it doesn't enclose each slice). This is possible because the slices have a fixed size. ### Multiple dimensions -The above description applies to a higher-rank right argument. As an example, we'll look at two-row slices of a shape `3βΏ4` array. For convenience, we will box each slice. Note that slices always have the same rank as the argument array. +The above description applies to a higher-rank right argument. As an example, we'll look at two-row slices of a shape `3βΏ4` array. For convenience, we will enclose each slice. Note that slices always have the same rank as the argument array. <β2 2β"0123"βΎ"abcd"β"ABCD" β diff --git a/docs/doc/context.html b/docs/doc/context.html index 22fe5443..f511c1ef 100644 --- a/docs/doc/context.html +++ b/docs/doc/context.html @@ -10,12 +10,12 @@ <p>In each case, some values are used as inputs to functions while others are the functions being applied. The result of a function can be used either as an input or as a function again. These expressions correspond to the APL expression where <code><span class='Value'>a</span></code> and <code><span class='Value'>e</span></code> are arrays, <code><span class='Value'>b</span></code> and <code><span class='Value'>c</span></code> are functions, and <code><span class='Value'>d</span></code> is a monadic operator. However, these syntactic classes have to be known to see what the APL expression is doingβthey are a form of context that is required for a reader to know the grammatical structure of the expression. In a context-free grammar like that of simple C or Lisp expressions, a value's grammatical role is part of the expression itself, indicated with parentheses: they come after the function in C and before it in Lisp. Of course, a consequence of using parentheses in this way is having a lot of parentheses. BQN uses a different method to annotate grammatical role:</p> <pre><span class='Value'>a</span> <span class='Function'>B</span> <span class='Function'>C</span> <span class='Modifier'>_d</span> <span class='Value'>e</span> </pre> -<p>Here, the lowercase spelling indicates that <code><span class='Value'>a</span></code> and <code><span class='Value'>e</span></code> are to be treated as values ("arrays" in APL) while the uppercase spelling of variables <code><span class='Function'>B</span></code> and <code><span class='Function'>C</span></code> are used as functions and <code><span class='Modifier'>_d</span></code> is a modifier ("monadic operator"). Like parentheses for function application, the spelling is not inherent to the variable values used, but instead indicates their grammatical role in this particular expression. A variable has no inherent spelling and can be used in any role, so the names <code><span class='Value'>a</span></code>, <code><span class='Function'>A</span></code>, <code><span class='Modifier'>_a</span></code>, and <code><span class='Composition'>_a_</span></code> all refer to exact same variable, but in different roles; typically we use the lowercase name to refer to the variable in isolation. While we still don't know anything about what values <code><span class='Value'>a</span></code>, <code><span class='Value'>b</span></code>, <code><span class='Value'>c</span></code>, and so on have, we know how they interact in the line of code above.</p> +<p>Here, the lowercase spelling indicates that <code><span class='Value'>a</span></code> and <code><span class='Value'>e</span></code> are to be treated as subjects ("arrays" in APL) while the uppercase spelling of variables <code><span class='Function'>B</span></code> and <code><span class='Function'>C</span></code> are used as functions and <code><span class='Modifier'>_d</span></code> is a 1-modifier ("monadic operator"). Like parentheses for function application, the spelling is not inherent to the variable values used, but instead indicates their grammatical role in this particular expression. A variable has no inherent spelling and can be used in any role, so the names <code><span class='Value'>a</span></code>, <code><span class='Function'>A</span></code>, <code><span class='Modifier'>_a</span></code>, and <code><span class='Modifier2'>_a_</span></code> all refer to exact same variable, but in different roles; typically we use the lowercase name to refer to the variable in isolationβall values are nouns when speaking about them in English. While we still don't know anything about what values <code><span class='Value'>a</span></code>, <code><span class='Value'>b</span></code>, <code><span class='Value'>c</span></code>, and so on have, we know how they interact in the line of code above.</p> <h2 id="is-grammatical-context-really-a-problem-">Is grammatical context really a problem?</h2> <p>Yes, in the sense of <a href="../problems.html">problems with BQN</a>. A grammar that uses context is harder for humans to read and machines to execute. A particular difficulty is that parts of an expression you don't yet understand can interfere with parts you do, making it difficult to work through an unknown codebase.</p> -<p>One difficulty beginners to APL will encounter is that code in APL at first appears like a string of undifferentiated symbols. For example, a tacit Unique Mask implementation <code><span class='Value'>β³β¨</span><span class='Function'>=</span><span class='Value'>β³</span><span class='Composition'>β</span><span class='Function'>β’</span></code> consists of six largely unfamiliar characters with little to distinguish them (in fact, the one obvious bit of structure, the repeated <code><span class='Value'>β³</span></code>, is misleading as it means different things in each case!). Simply placing parentheses into the expression, like <code><span class='Paren'>(</span><span class='Value'>β³β¨</span><span class='Paren'>)</span><span class='Function'>=</span><span class='Paren'>(</span><span class='Value'>β³</span><span class='Composition'>β</span><span class='Function'>β’</span><span class='Paren'>)</span></code>, can be a great help to a beginner, and part of learning APL is to naturally see where the parentheses should go. The equivalent BQN expression, <code><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>=β</span><span class='Composition'>β</span><span class='Function'>β </span></code>, will likely appear equally intimidating at first, but the path to learning which things apply to which is much shorter: rather than learning the entire list of APL primitives, a beginner just needs to know that superscript characters like <code><span class='Modifier'>Λ</span></code> are modifiers and characters like <code><span class='Composition'>β</span></code> with unbroken circles are compositions before beginning to learn the BQN grammar that will explain how to tie the various parts together.</p> +<p>One difficulty beginners to APL will encounter is that code in APL at first appears like a string of undifferentiated symbols. For example, a tacit Unique Mask implementation <code><span class='Value'>β³β¨</span><span class='Function'>=</span><span class='Value'>β³</span><span class='Modifier2'>β</span><span class='Function'>β’</span></code> consists of six largely unfamiliar characters with little to distinguish them (in fact, the one obvious bit of structure, the repeated <code><span class='Value'>β³</span></code>, is misleading as it means different things in each case!). Simply placing parentheses into the expression, like <code><span class='Paren'>(</span><span class='Value'>β³β¨</span><span class='Paren'>)</span><span class='Function'>=</span><span class='Paren'>(</span><span class='Value'>β³</span><span class='Modifier2'>β</span><span class='Function'>β’</span><span class='Paren'>)</span></code>, can be a great help to a beginner, and part of learning APL is to naturally see where the parentheses should go. The equivalent BQN expression, <code><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>=β</span><span class='Modifier2'>β</span><span class='Function'>β </span></code>, will likely appear equally intimidating at first, but the path to learning which things apply to which is much shorter: rather than learning the entire list of APL primitives, a beginner just needs to know that superscript characters like <code><span class='Modifier'>Λ</span></code> are 1-modifiers and characters like <code><span class='Modifier2'>β</span></code> with unbroken circles are 2-modifiers before beginning to learn the BQN grammar that will explain how to tie the various parts together.</p> <p>This sounds like a distant concern to a master of APL or a computer that has no difficulty memorizing a few dozen glyphs. Quite the opposite: the same concern applies to variables whenever you begin work with an unfamiliar codebase! Many APL programmers even enforce variable name conventions to ensure they know the class of a variable. By having such a system built in, BQN keeps you from having to rely on programmers following a style guide, and also allows greater flexibility, including <a href="functional.html">functional programming</a>, as we'll see later.</p> -<p>Shouldn't a codebase define all the variables it uses, so we can see their class from the definition? Not always: consider that in a language with libraries, code might be imported from dependencies. Many APLs also have some dynamic features that can allow a variable to have more than one class, such as the <code><span class='Value'>βΊ</span><span class='Gets'>β</span><span class='Function'>β’</span></code> pattern in a dfn that makes <code><span class='Value'>βΊ</span></code> an array in the dyadic case but a function in the monadic case. Regardless, searching for a definition somewhere in the code is certainly a lot more work than knowing the class right away! One final difficulty is that even one unknown can delay understanding of an entire expression. Suppose in <code><span class='Function'>A</span> <span class='Function'>B</span> <span class='Value'>c</span></code>, <code><span class='Function'>B</span></code> is a function and <code><span class='Value'>c</span></code> is an array, and both values are known to be constant. If <code><span class='Function'>A</span></code> is known to be a function (even if its value is not yet known), its right argument <code><span class='Function'>B</span> <span class='Value'>c</span></code> can be evaluated ahead of time. But if <code><span class='Function'>A</span></code>'s type isn't known, it's impossible to know if this optimization is worth it, because if it is an array, <code><span class='Function'>B</span></code> will instead be called dyadically.</p> +<p>Shouldn't a codebase define all the variables it uses, so we can see their class from the definition? Not always: consider that in a language with libraries, code might be imported from dependencies. Many APLs also have some dynamic features that can allow a variable to have more than one class, such as the <code><span class='Value'>βΊ</span><span class='Gets'>β</span><span class='Function'>β’</span></code> pattern in a dfn that makes <code><span class='Value'>βΊ</span></code> an array in the dyadic case but a function in the monadic case. Regardless, searching for a definition somewhere in the code is certainly a lot more work than knowing the class just from looking! One final difficulty is that even one unknown can delay understanding of an entire expression. Suppose in <code><span class='Function'>A</span> <span class='Function'>B</span> <span class='Value'>c</span></code>, <code><span class='Function'>B</span></code> is a function and <code><span class='Value'>c</span></code> is an array, and both values are known to be constant. If <code><span class='Function'>A</span></code> is known to be a function (even if its value is not yet known), its right argument <code><span class='Function'>B</span> <span class='Value'>c</span></code> can be evaluated ahead of time. But if <code><span class='Function'>A</span></code>'s type isn't known, it's impossible to know if this optimization is worth it, because if it is an array, <code><span class='Function'>B</span></code> will instead be called dyadically.</p> <h2 id="bqn-s-spelling-system">BQN's spelling system</h2> <p>BQN's expression grammar is a simplified version of the typical APL, removing some oddities like niladic functions and the two-glyph Outer Product operator. Every value can be used in any of four syntactic roles:</p> <table> @@ -28,7 +28,7 @@ </thead> <tbody> <tr> -<td>Value</td> +<td>Subject</td> <td>Array</td> <td>Noun</td> </tr> @@ -38,24 +38,24 @@ <td>Verb</td> </tr> <tr> -<td>Modifier</td> +<td>1-modifier</td> <td>Monadic operator</td> <td>Adverb</td> </tr> <tr> -<td>Composition</td> +<td>2-modifier</td> <td>Dyadic operator</td> <td>Conjunction</td> </tr> </tbody> </table> -<p>Unlike variables, BQN primitives have only one spelling, and a fixed role (but their values can be used in a different role by storing them in variables). Superscript glyphs <code><span class='Modifier'>ΛΒ¨ΛβΌβΒ΄`</span></code> are used for modifiers, and glyphs <code><span class='Composition'>βββΈββΎββΆβββ</span></code> with an unbroken circle are compositions. Other primitives are functions. String and numeric literals are values.</p> -<p>BQN's variables use another system, where the spelling indicates how the variable's value is used. A variable spelled with a lowercase first letter, like <code><span class='Value'>var</span></code>, is a value. Spelled with an uppercase first letter, like <code><span class='Function'>Var</span></code>, it is a function. Underscores are placed where operands apply to indicate a modifier <code><span class='Modifier'>_var</span></code> or composition <code><span class='Composition'>_var_</span></code>. Other than the first letter or underscore, variables are case-insensitive.</p> +<p>Unlike variables, BQN primitives have only one spelling, and a fixed role (but their values can be used in a different role by storing them in variables). Superscript glyphs <code><span class='Modifier'>ΛΒ¨ΛβΌβΒ΄`</span></code> are used for 1-modifiers, and glyphs <code><span class='Modifier2'>βββΈββΎββΆβββ</span></code> with an unbroken circle are 2-modifiers. Other primitives are functions. String and numeric literals are subjects.</p> +<p>BQN's variables use another system, where the spelling indicates how the variable's value is used. A variable spelled with a lowercase first letter, like <code><span class='Value'>var</span></code>, is a subject. Spelled with an uppercase first letter, like <code><span class='Function'>Var</span></code>, it is a function. Underscores are placed where operands apply to indicate a 1-modifier <code><span class='Modifier'>_var</span></code> or 2-modifier <code><span class='Modifier2'>_var_</span></code>. Other than the first letter or underscore, variables are case-insensitive.</p> <p>The associations between spelling and syntactic role are considered part of BQN's <a href="../spec/token.html">token formation rules</a>.</p> -<p>One rule for typing is also best considered to be a pre-parsing rule like the spelling system: the role of a brace construct <code><span class='Brace'>{}</span></code> with no header is determined by which special arguments it uses: it's a value if there are none, but a <code><span class='Value'>π¨</span></code> or <code><span class='Value'>π©</span></code> makes it at least a function, an <code><span class='Function'>π½</span></code> makes it a modifier or composition, and a <code><span class='Function'>πΎ</span></code> always makes it a composition.</p> +<p>One rule for typing is also best considered to be a pre-parsing rule like the spelling system: the role of a brace construct <code><span class='Brace'>{}</span></code> with no header is determined by which special arguments it uses: it's a subject if there are none, but a <code><span class='Value'>π¨</span></code> or <code><span class='Value'>π©</span></code> makes it at least a function, an <code><span class='Function'>π½</span></code> makes it a 1- or 2-modifier, and a <code><span class='Function'>πΎ</span></code> always makes it a 2-modifier.</p> <h2 id="bqn-s-grammar">BQN's grammar</h2> <p>A formal treatment is included in <a href="../spec/grammar.html">the spec</a>. BQN's grammarβthe ways syntactic roles interactβfollows the original APL model (plus trains) closely, with allowances for new features like list notation. In order to keep BQN's syntax context-free, the syntactic role of any expression must be known from its contents, just like tokens.</p> -<p>Here is a table of the APL-derived operator and function application rules:</p> +<p>Here is a table of the APL-derived modifier and function application rules:</p> <table> <thead> <tr> @@ -71,14 +71,14 @@ <td></td> <td><code><span class='Function'>F</span></code></td> <td><code><span class='Value'>x</span></code></td> -<td>Value</td> +<td>Subject</td> <td>Monadic function</td> </tr> <tr> <td><code><span class='Value'>w</span></code></td> <td><code><span class='Function'>F</span></code></td> <td><code><span class='Value'>x</span></code></td> -<td>Value</td> +<td>Subject</td> <td>Dyadic function</td> </tr> <tr> @@ -100,39 +100,39 @@ <td><code><span class='Modifier'>_m</span></code></td> <td></td> <td>Function</td> -<td>Modifier</td> +<td>1-Modifier</td> </tr> <tr> <td><code><span class='Function'>F</span><span class='Value'>*</span></code></td> -<td><code><span class='Composition'>_c_</span></code></td> +<td><code><span class='Modifier2'>_c_</span></code></td> <td><code><span class='Function'>G</span><span class='Value'>*</span></code></td> <td>Function</td> -<td>Composition</td> +<td>2-Modifier</td> </tr> <tr> <td></td> -<td><code><span class='Composition'>_c_</span></code></td> +<td><code><span class='Modifier2'>_c_</span></code></td> <td><code><span class='Function'>G</span><span class='Value'>*</span></code></td> -<td>Modifier</td> +<td>1-Modifier</td> <td>Partial application</td> </tr> <tr> <td><code><span class='Function'>F</span><span class='Value'>*</span></code></td> -<td><code><span class='Composition'>_c_</span></code></td> +<td><code><span class='Modifier2'>_c_</span></code></td> <td></td> -<td>Modifier</td> +<td>1-Modifier</td> <td>Partial application</td> </tr> </tbody> </table> -<p>A function with an asterisk indicates that a value can also be used: in these positions there is no difference between function and value spellings. Operator applications bind more tightly than functions, and associate left-to-right while functions associate right-to-left.</p> -<p>BQN lists can be written with angle brackets <code><span class='Bracket'>β¨</span><span class='Value'>elt0</span><span class='Separator'>,</span><span class='Value'>elt1</span><span class='Separator'>,</span><span class='Value'>β¦</span><span class='Bracket'>β©</span></code> or ligatures <code><span class='Value'>elt0</span><span class='Ligature'>βΏ</span><span class='Value'>elt1</span><span class='Ligature'>βΏ</span><span class='Value'>β¦</span></code>. In either case the elements can have any type, and the result is a value.</p> -<p>The statements in a brace block, function, or operator can also be any role, including the return value at the end. These roles have no effect: outside of braces, a function always returns an array, a modifier always returns a function, and so on, regardless of how these objects were defined.</p> +<p>A function with an asterisk indicates that a subject can also be used: in these positions there is no difference between function and subject spellings. Modifier applications bind more tightly than functions, and associate left-to-right while functions associate right-to-left.</p> +<p>BQN lists can be written with angle brackets <code><span class='Bracket'>β¨</span><span class='Value'>elt0</span><span class='Separator'>,</span><span class='Value'>elt1</span><span class='Separator'>,</span><span class='Value'>β¦</span><span class='Bracket'>β©</span></code> or ligatures <code><span class='Value'>elt0</span><span class='Ligature'>βΏ</span><span class='Value'>elt1</span><span class='Ligature'>βΏ</span><span class='Value'>β¦</span></code>. In either case the elements can have any type, and the result is a subject.</p> +<p>The statements in a block can also be any role, including the return value at the end. These roles have no effect: outside of braces, an immediate block is a subject, a function always returns a subject, and a modifier always returns a function, regardless of how these objects were defined.</p> <h2 id="mixing-roles">Mixing roles</h2> -<p>BQN's basic types align closely with its syntactic roles: functions, modifiers, and compositions are all basic types, while values are split into numbers, characters, and arrays. This is no accident, and usually values will be used in roles that match their underlying type. However, the ability to use a role that doesn't match the type is very useful.</p> -<p>Any type can be passed as an argument to a function, or as an operand, by treating it as a value. This means that BQN fully supports Lisp-style <a href="functional.html">functional programming</a>, where functions can be used as values.</p> -<p>It can also be useful to treat a value type as a function, in which case it applies as a constant function. This rule is useful with most built-in operators. For example, <code><span class='Function'>F</span><span class='Composition'>β</span><span class='Number'>1</span></code> uses a constant for the rank even though in general a function can be given, and <code><span class='Value'>a</span><span class='Composition'>βΎ</span><span class='Paren'>(</span><span class='Value'>b</span><span class='Composition'>βΈ</span><span class='Function'>/</span><span class='Paren'>)</span></code> inserts the values in <code><span class='Value'>a</span></code> into the positions selected by <code><span class='Value'>b</span></code>, ignoring the old values rather than applying a function to them.</p> -<p>Other mixes of roles are generally not useful. While a combination such as treating a function as a modifier is allowed, attempting to apply it to an operand will fail. Only a modifier can be applied as a modifier and only a composition can be applied as a composition. Only a function or value can be applied as a function.</p> -<p>It's also worth noting that something that appears to be a value may actually be a function! For example, the result of <code><span class='Value'>π¨</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code> may not always be <code><span class='Value'>π¨</span></code>. <code><span class='Value'>π¨</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code> is exactly identical to <code><span class='Function'>π</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code>, which gives <code><span class='Value'>π©</span><span class='Function'>π</span><span class='Value'>π©</span></code>. If <code><span class='Function'>π</span></code> is a number, character, or array, that's the same as <code><span class='Value'>π¨</span></code>, but if it is a function, then it will be applied.</p> -<p>The primary way to change the role of a value in BQN is to use a name, including one of the arguments to a brace function. In particular, you can use <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code> to convert a value operand into a function. Converting a function to a value is more difficult. Often an array of functions is wanted, in which case they can be stranded together; otherwise it's probably best to give the function a name. Picking a function out of a list, for example <code><span class='Function'>β</span><span class='Bracket'>β¨</span><span class='Function'>+</span><span class='Bracket'>β©</span></code> will give it as a value.</p> +<p>BQN's value types align closely with its syntactic roles: functions, 1-modifiers, and 2-modifiers are all types (<em>operation</em> types) as well as roles, while the other types (<em>data</em> types) are split into numbers, characters, and arrays. This is no accident, and usually values will be used in roles that correspond to their underlying type. However, the ability to use a role that doesn't match the type is also useful.</p> +<p>Any type can be passed as an argument to a function, or as an operand, by treating it as a subject. This means that BQN fully supports Lisp-style <a href="functional.html">functional programming</a>, where functions can be used as first-class entities.</p> +<p>It can also be useful to treat a value of a data type as a function, in which case it applies as a constant function. This rule is useful with most built-in modifiers. For example, <code><span class='Function'>F</span><span class='Modifier2'>β</span><span class='Number'>1</span></code> uses a constant for the rank even though in general a function can be given, and if <code><span class='Value'>a</span></code> is an array then <code><span class='Value'>a</span><span class='Modifier2'>βΎ</span><span class='Paren'>(</span><span class='Value'>b</span><span class='Modifier2'>βΈ</span><span class='Function'>/</span><span class='Paren'>)</span></code> inserts the values in <code><span class='Value'>a</span></code> into the positions selected by <code><span class='Value'>b</span></code>, ignoring the old values rather than applying a function to them.</p> +<p>Other mixes of roles are generally not useful. While a combination such as treating a function as a modifier is allowed, attempting to apply it to an operand will fail. Only a 1-modifier can be applied as a 1-modifier and only a 2-modifier can be applied as a 2-modifier. Only a function or data can be applied as a function.</p> +<p>It's also worth noting that a subject may unexpectedly be a function! For example, the result of <code><span class='Value'>π¨</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code> may not always be <code><span class='Value'>π¨</span></code>. <code><span class='Value'>π¨</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code> is exactly identical to <code><span class='Function'>π</span><span class='Modifier'>Λ</span><span class='Value'>π©</span></code>, which gives <code><span class='Value'>π©</span><span class='Function'>π</span><span class='Value'>π©</span></code>. If <code><span class='Function'>π</span></code> is a number, character, or array, that's the same as <code><span class='Value'>π¨</span></code>, but if it is a function, then it will be applied.</p> +<p>The primary way to change the role of a value in BQN is to use a name, including one of the special names for inputs to a brace function or modifier. In particular, you can use <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code> to convert a subject operand into a function. Converting a function to a subject is more difficult. Often an array of functions is wanted, in which case they can be stranded together; otherwise it's probably best to give the function a name. Picking a function out of a list, for example <code><span class='Function'>β</span><span class='Bracket'>β¨</span><span class='Function'>+</span><span class='Bracket'>β©</span></code>, will give it as a subject.</p> diff --git a/docs/doc/depth.html b/docs/doc/depth.html index 9becdb67..c99a75e4 100644 --- a/docs/doc/depth.html +++ b/docs/doc/depth.html @@ -1,6 +1,6 @@ <head><link href="../style.css" rel="stylesheet"/></head> <h1 id="depth">Depth</h1> -<p>The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (<code><span class='Function'>β‘</span></code>) returns the depth of its argument, while the composition Depth (<code><span class='Composition'>β</span></code>) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes.</p> +<p>The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (<code><span class='Function'>β‘</span></code>) returns the depth of its argument, while the 2-modifier Depth (<code><span class='Modifier2'>β</span></code>) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes.</p> <h2 id="the-depth-function">The Depth function</h2> <p>To find the depth of an array, use Depth (<code><span class='Function'>β‘</span></code>). For example, the depth of a list of numbers or characters is 1:</p> <pre> <span class='Function'>β‘</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span> @@ -8,7 +8,7 @@ <span class='Function'>β‘</span> <span class='String'>"a string is a list of characters"</span> <span class='Number'>1</span> </pre> -<p>Depth is somewhat analogous to an array's rank <code><span class='Function'>β β’</span><span class='Value'>π©</span></code>, and in fact rank can be "converted" to depth by splitting rows with <code><span class='Function'><</span><span class='Composition'>β</span><span class='Number'>1</span></code>, reducing the rank by 1 and increasing the depth. Unlike rank, Depth doesn't care at all about its argument's shape:</p> +<p>Depth is somewhat analogous to an array's rank <code><span class='Function'>β β’</span><span class='Value'>π©</span></code>, and in fact rank can be "converted" to depth by splitting rows with <code><span class='Function'><</span><span class='Modifier2'>β</span><span class='Number'>1</span></code>, reducing the rank by 1 and increasing the depth. Unlike rank, Depth doesn't care at all about its argument's shape:</p> <pre> <span class='Function'>β‘</span> <span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span><span class='Function'>β₯</span><span class='String'>"characters"</span> <span class='Number'>1</span> <span class='Function'>β‘</span> <span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>+β</span><span class='Number'>10</span><span class='Paren'>)</span><span class='Function'>β₯</span><span class='String'>"characters"</span> @@ -22,7 +22,7 @@ <span class='Function'>β‘</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Function'><</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Function'><<<</span><span class='Number'>5</span><span class='Bracket'>β©</span> <span class='Number'>4</span> </pre> -<p>As the above expressions suggest, the depth of an array is the maximum of its elements, plus one. The base case, a non-array (including a function, modifier, or combinator), has depth 0.</p> +<p>As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, a non-array (including a function or modifier), has depth 0.</p> <pre> <span class='Function'>β‘</span><span class='String'>'c'</span> <span class='Number'>0</span> <span class='Function'>F</span><span class='Gets'>β</span><span class='Function'>+</span><span class='Separator'>β</span><span class='Function'>β‘</span><span class='Value'>f</span> @@ -32,8 +32,8 @@ <span class='Function'>β‘</span><span class='Bracket'>β¨</span><span class='Number'>5</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='String'>'c'</span><span class='Separator'>,</span><span class='Value'>f</span><span class='Separator'>,</span><span class='Number'>2</span><span class='Bracket'>β©β©</span> <span class='Number'>2</span> </pre> -<p>If the function <code><span class='Function'>IsArray</span></code> indicates whether its argument is an array, then we can write a recursive definition of Depth using the Choose composition.</p> -<pre><span class='Function'>Depth</span><span class='Gets'>β</span><span class='Function'>IsArray</span><span class='Composition'>βΆ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Brace'>{</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>0</span><span class='Function'>β</span><span class='Modifier'>Β΄</span><span class='Function'>Depth</span><span class='Modifier'>Β¨</span><span class='Function'>β₯</span><span class='Value'>π©</span><span class='Brace'>}</span> +<p>If the function <code><span class='Function'>IsArray</span></code> indicates whether its argument is an array, then we can write a recursive definition of Depth using the Choose modifier.</p> +<pre><span class='Function'>Depth</span><span class='Gets'>β</span><span class='Function'>IsArray</span><span class='Modifier2'>βΆ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Brace'>{</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>0</span><span class='Function'>β</span><span class='Modifier'>Β΄</span><span class='Function'>Depth</span><span class='Modifier'>Β¨</span><span class='Function'>β₯</span><span class='Value'>π©</span><span class='Brace'>}</span> </pre> <p>The minimum element depth of 0 implies that an empty array's depth is 1.</p> <pre> <span class='Function'>β‘</span><span class='Bracket'>β¨β©</span> @@ -74,22 +74,22 @@ <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>4</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>β</span> </pre> -<p>This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a vector, and convert any non-array argument into an array by boxing it:</p> -<pre> <span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Bracket'>β©</span> <span class='Function'><</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>=β‘</span><span class='Paren'>)</span><span class='Modifier'>Β¨</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Function'>β</span><span class='Number'>6</span><span class='Ligature'>βΏ</span><span class='Number'>7</span> +<p>This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a list, and convert any non-array argument into an array by enclosing it:</p> +<pre> <span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Bracket'>β©</span> <span class='Function'><</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>=β‘</span><span class='Paren'>)</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Function'>β</span><span class='Number'>6</span><span class='Ligature'>βΏ</span><span class='Number'>7</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> <p>While very consistent, this extension represents a small convenience and makes it difficult to act on a single axis, which for Replicate and <a href="group.html">Group</a> is probably the most common way the primitive is used:</p> <pre> <span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span> <span class='Function'>/</span> <span class='String'>"abcde"</span> <span class='Value'>[</span> <span class='Value'>aaabbcddeee</span> <span class='Value'>]</span> </pre> -<p>With the extension above, every case like this would have to use <code><span class='Function'><</span><span class='Composition'>βΈ</span><span class='Function'>/</span></code> instead of just <code><span class='Function'>/</span></code>. BQN avoids this difficulty by testing the left argument's depth. A depth-1 argument applies to the first axis only, giving the behavior above.</p> +<p>With the extension above, every case like this would have to use <code><span class='Function'><</span><span class='Modifier2'>βΈ</span><span class='Function'>/</span></code> instead of just <code><span class='Function'>/</span></code>. BQN avoids this difficulty by testing the left argument's depth. A depth-1 argument applies to the first axis only, giving the behavior above.</p> <p>For Select, the depth-1 case is still quite useful, but it may also be desirable to choose a single cell using a list of numbers. In this case the left argument depth can be increased from the bottom using <code><span class='Function'><</span><span class='Modifier'>Β¨</span></code>.</p> -<pre> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>4</span> <span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Function'>β</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>5</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> +<pre> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>4</span> <span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Function'>β</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>5</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Number'>4</span> <span class='Number'>0</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Number'>4</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> -<h2 id="the-depth-composition">The Depth composition</h2> -<p>The Depth composition (<code><span class='Composition'>β</span></code>) is a generalization of Each that allows diving deeper into an array. To illustrate it we'll use a shape <code><span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>3</span></code> array of lists of lists.</p> -<pre> <span class='Function'>β’</span> <span class='Value'>n</span> <span class='Gets'>β</span> <span class='Function'><</span><span class='Composition'>β</span><span class='Number'>1</span><span class='Composition'>β</span><span class='Number'>2</span> <span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Function'>β₯β</span><span class='Number'>48</span> +<h2 id="the-depth-modifier">The Depth modifier</h2> +<p>The Depth 2-modifier (<code><span class='Modifier2'>β</span></code>) is a generalization of Each that allows diving deeper into an array. To illustrate it we'll use a shape <code><span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>3</span></code> array of lists of lists.</p> +<pre> <span class='Function'>β’</span> <span class='Value'>n</span> <span class='Gets'>β</span> <span class='Function'><</span><span class='Modifier2'>β</span><span class='Number'>1</span><span class='Modifier2'>β</span><span class='Number'>2</span> <span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Function'>β₯β</span><span class='Number'>48</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>6</span> <span class='Number'>7</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>8</span> <span class='Number'>9</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>10</span> <span class='Number'>11</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>12</span> <span class='Number'>13</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>14</span> <span class='Number'>15</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>16</span> <span class='Number'>17</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>18</span> <span class='Number'>19</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>20</span> <span class='Number'>21</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>22</span> <span class='Number'>23</span> <span class='Value'>]</span> <span class='Value'>]</span> @@ -109,14 +109,14 @@ <span class='Value'>β</span> </pre> <p>Depth <code><span class='Number'>Β―1</span></code> is equivalent to Each, and reverses the larger vectors, while depth <code><span class='Number'>Β―2</span></code> applies Each twice to reverse the smaller vectors:</p> -<pre> <span class='Function'>β½</span><span class='Composition'>β</span><span class='Number'>Β―1</span> <span class='Value'>n</span> +<pre> <span class='Function'>β½</span><span class='Modifier2'>β</span><span class='Number'>Β―1</span> <span class='Value'>n</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>6</span> <span class='Number'>7</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>10</span> <span class='Number'>11</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>8</span> <span class='Number'>9</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>14</span> <span class='Number'>15</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>12</span> <span class='Number'>13</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>18</span> <span class='Number'>19</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>16</span> <span class='Number'>17</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>22</span> <span class='Number'>23</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>20</span> <span class='Number'>21</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>26</span> <span class='Number'>27</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>24</span> <span class='Number'>25</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>30</span> <span class='Number'>31</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>28</span> <span class='Number'>29</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>34</span> <span class='Number'>35</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>32</span> <span class='Number'>33</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>38</span> <span class='Number'>39</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>36</span> <span class='Number'>37</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>42</span> <span class='Number'>43</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>40</span> <span class='Number'>41</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>46</span> <span class='Number'>47</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>44</span> <span class='Number'>45</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>β</span> - <span class='Function'>β½</span><span class='Composition'>β</span><span class='Number'>Β―2</span> <span class='Value'>n</span> + <span class='Function'>β½</span><span class='Modifier2'>β</span><span class='Number'>Β―2</span> <span class='Value'>n</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>2</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>5</span> <span class='Number'>4</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>7</span> <span class='Number'>6</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>9</span> <span class='Number'>8</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>11</span> <span class='Number'>10</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>13</span> <span class='Number'>12</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>15</span> <span class='Number'>14</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>17</span> <span class='Number'>16</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>19</span> <span class='Number'>18</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Number'>21</span> <span class='Number'>20</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>23</span> <span class='Number'>22</span> <span class='Value'>]</span> <span class='Value'>]</span> @@ -125,11 +125,11 @@ <span class='Value'>β</span> </pre> <p>While a negative depth tells how many levels to go down, a non-negative depth gives the maximum depth of the argument before applying the left operand. On a depth-3 array like above, depth <code><span class='Number'>2</span></code> is equivalent to <code><span class='Number'>Β―1</span></code> and depth <code><span class='Number'>1</span></code> is equivalent to <code><span class='Number'>Β―2</span></code>. A depth of <code><span class='Number'>0</span></code> means to loop until non-arrays are reached, that is, apply <a href="https://aplwiki.com/wiki/Pervasion">pervasively</a>, like a scalar function.</p> -<pre> <span class='Bracket'>β¨</span><span class='String'>'a'</span><span class='Separator'>,</span><span class='String'>"bc"</span><span class='Bracket'>β©</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Number'>0</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©</span> +<pre> <span class='Bracket'>β¨</span><span class='String'>'a'</span><span class='Separator'>,</span><span class='String'>"bc"</span><span class='Bracket'>β©</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Number'>0</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Number'>2</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Number'>3</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>b</span> <span class='Number'>4</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>c</span> <span class='Number'>4</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> <p>With a positive operand, Depth doesn't have to use the same depth everywhere. Here, Length is applied as soon as the depth for a particular element is 1 or less, including if the argument has depth 0. For example, it maps over <code><span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©β©</span></code>, but not over <code><span class='Bracket'>β¨</span><span class='Number'>11</span><span class='Separator'>,</span><span class='Number'>12</span><span class='Bracket'>β©</span></code>, even though these are elements of the same array.</p> -<pre> <span class='Function'>β </span><span class='Composition'>β</span><span class='Number'>1</span> <span class='Bracket'>β¨</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>5</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>7</span><span class='Bracket'>β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>8</span><span class='Separator'>,</span><span class='Number'>9</span><span class='Separator'>,</span><span class='Number'>10</span><span class='Bracket'>β©β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>11</span><span class='Separator'>,</span><span class='Number'>12</span><span class='Bracket'>β©β©</span> +<pre> <span class='Function'>β </span><span class='Modifier2'>β</span><span class='Number'>1</span> <span class='Bracket'>β¨</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>5</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>7</span><span class='Bracket'>β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>8</span><span class='Separator'>,</span><span class='Number'>9</span><span class='Separator'>,</span><span class='Number'>10</span><span class='Bracket'>β©β©</span><span class='Separator'>,</span><span class='Bracket'>β¨</span><span class='Number'>11</span><span class='Separator'>,</span><span class='Number'>12</span><span class='Bracket'>β©β©</span> <span class='Value'>[</span> <span class='Number'>1</span> <span class='Value'>[</span> <span class='Number'>1</span> <span class='Number'>2</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Number'>1</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Value'>]</span> <span class='Number'>2</span> <span class='Value'>]</span> </pre> diff --git a/docs/doc/fromDyalog.html b/docs/doc/fromDyalog.html index 57196984..ae6fc64b 100644 --- a/docs/doc/fromDyalog.html +++ b/docs/doc/fromDyalog.html @@ -25,7 +25,7 @@ <tr> <td>Monad</td> <td><code><span class='Value'>*</span></code></td> -<td><code><span class='Value'>*</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>Γ·</span><span class='Number'>2</span><span class='Paren'>)</span></code></td> +<td><code><span class='Value'>*</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>Γ·</span><span class='Number'>2</span><span class='Paren'>)</span></code></td> <td><code><span class='Value'>[</span><span class='Function'>β</span><span class='Value'>]</span></code></td> <td><code><span class='Value'>[</span><span class='Function'>β</span><span class='Value'>]</span></code></td> <td><code><span class='Value'>~</span></code></td> @@ -40,7 +40,7 @@ <tr> <td>Dyad</td> <td><code><span class='Value'>*</span></code></td> -<td><code><span class='Value'>*</span><span class='Composition'>β</span><span class='Function'>Γ·</span><span class='Value'>β¨</span></code></td> +<td><code><span class='Value'>*</span><span class='Modifier2'>β</span><span class='Function'>Γ·</span><span class='Value'>β¨</span></code></td> <td><code><span class='Function'>β§</span></code></td> <td><code><span class='Function'>β¨</span></code></td> <td><code><span class='Number'>1</span><span class='Function'>+-</span></code></td> @@ -108,7 +108,7 @@ </tr> </tbody> </table> -<p>Modifiers and combinators are a little harder. Many have equivalents in some cases, but Dyalog sometimes chooses different functionality based on whether the operand is an array. In BQN an array is always treated as a constant function.</p> +<p>Modifiers are a little harder. Many have equivalents in some cases, but Dyalog sometimes chooses different functionality based on whether the operand is an array. In BQN an array is always treated as a constant function.</p> <table> <thead> <tr> @@ -116,55 +116,55 @@ <th><code><span class='Modifier'>Β¨</span></code></th> <th><code><span class='Modifier'>β</span></code></th> <th><code><span class='Modifier'>Β΄</span></code></th> -<th><code><span class='Composition'>β</span></code></th> -<th><code><span class='Composition'>β</span></code></th> +<th><code><span class='Modifier2'>β</span></code></th> +<th><code><span class='Modifier2'>β</span></code></th> <th><code><span class='Modifier'>Λ</span></code></th> -<th><code><span class='Composition'>β</span></code></th> -<th><code><span class='Composition'>β</span></code></th> -<th><code><span class='Composition'>β</span></code></th> +<th><code><span class='Modifier2'>β</span></code></th> +<th><code><span class='Modifier2'>β</span></code></th> +<th><code><span class='Modifier2'>β</span></code></th> </tr> </thead> <tbody> <tr> <td>Dyalog</td> <td><code><span class='Modifier'>Β¨</span></code></td> -<td><code><span class='Composition'>β</span><span class='Number'>.</span></code></td> +<td><code><span class='Modifier2'>β</span><span class='Number'>.</span></code></td> <td><code><span class='Value'>βΏ</span></code></td> <td><code><span class='Value'>β€</span></code></td> <td><code><span class='Value'>β£</span></code></td> <td><code><span class='Value'>β¨</span></code></td> <td><code><span class='Value'>β€</span></code></td> <td><code><span class='Value'>β₯</span></code></td> -<td><code><span class='Composition'>β</span></code></td> +<td><code><span class='Modifier2'>β</span></code></td> </tr> </tbody> </table> -<p>In BQN <code><span class='Composition'>β</span></code> is Rank and <code><span class='Composition'>β</span></code> is Atop. Dyalog's Atop (<code><span class='Value'>β€</span></code>) and Over (<code><span class='Value'>β₯</span></code>) were added in version 18.0.</p> +<p>In BQN <code><span class='Modifier2'>β</span></code> is Rank and <code><span class='Modifier2'>β</span></code> is Atop. Dyalog's Atop (<code><span class='Value'>β€</span></code>) and Over (<code><span class='Value'>β₯</span></code>) were added in version 18.0.</p> <h2 id="for-writing">For writing</h2> -<p>The tables below give approximate implementations of Dyalog primitives for the ones that aren't the same. First- and last-axis pairs are also mostly omitted. BQN just has the first-axis form, and you can get the last-axis form with <code><span class='Composition'>β</span><span class='Number'>1</span></code>.</p> +<p>The tables below give approximate implementations of Dyalog primitives for the ones that aren't the same. First- and last-axis pairs are also mostly omitted. BQN just has the first-axis form, and you can get the last-axis form with <code><span class='Modifier2'>β</span><span class='Number'>1</span></code>.</p> <table> <tr><th colspan=3>Functions</th></tr> <tr><th> Glyph </th><th> Monadic </th><th> Dyadic </th> </tr> <tr><td> <code><span class='Value'>*</span></code> </td><td colspan=2><code><span class='Function'>β</span></code></td> </tr> -<tr><td> <code><span class='Composition'>β</span></code> </td><td colspan=2><code><span class='Function'>β</span><span class='Modifier'>βΌ</span></code></td> </tr> +<tr><td> <code><span class='Modifier2'>β</span></code> </td><td colspan=2><code><span class='Function'>β</span><span class='Modifier'>βΌ</span></code></td> </tr> <tr><td> <code><span class='Function'>!</span></code> </td><td colspan=2>Implement it yourself</td> </tr> -<tr><td> <code><span class='Composition'>β</span></code> </td><td colspan=2>Some complex exponential stuff, maybe</td> </tr> -<tr><td> <code><span class='Value'>~</span></code> </td><td> <code><span class='Function'>Β¬</span></code> </td><td> <code><span class='Function'>Β¬</span><span class='Composition'>β</span><span class='Function'>β/β£</span></code></td> </tr> +<tr><td> <code><span class='Modifier2'>β</span></code> </td><td colspan=2>Some complex exponential stuff, maybe</td> </tr> +<tr><td> <code><span class='Value'>~</span></code> </td><td> <code><span class='Function'>Β¬</span></code> </td><td> <code><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β/β£</span></code></td> </tr> <tr><td> <code><span class='Value'>?</span></code> </td><td colspan=2>Library?</td> </tr> -<tr><td> <code><span class='Value'>β²</span></code> </td><td> </td><td> <code><span class='Function'>Β¬</span><span class='Composition'>β</span><span class='Function'>β§</span></code></td> </tr> -<tr><td> <code><span class='Value'>β±</span></code> </td><td> </td><td> <code><span class='Function'>Β¬</span><span class='Composition'>β</span><span class='Function'>β¨</span></code></td> </tr> +<tr><td> <code><span class='Value'>β²</span></code> </td><td> </td><td> <code><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β§</span></code></td> </tr> +<tr><td> <code><span class='Value'>β±</span></code> </td><td> </td><td> <code><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β¨</span></code></td> </tr> <tr><td> <code><span class='Value'>β΄</span></code> </td><td> <code><span class='Function'>β’</span></code> </td><td> <code><span class='Function'>β₯</span></code></td> </tr> -<tr><td> <code><span class='Separator'>,</span></code> </td><td> <code><span class='Function'>β₯</span></code> </td><td> <code><span class='Function'>βΎ</span><span class='Composition'>β</span><span class='Number'>1</span></code></td> </tr> +<tr><td> <code><span class='Separator'>,</span></code> </td><td> <code><span class='Function'>β₯</span></code> </td><td> <code><span class='Function'>βΎ</span><span class='Modifier2'>β</span><span class='Number'>1</span></code></td> </tr> <tr><td> <code><span class='Value'>βͺ</span></code> </td><td> <code><span class='Function'>β₯</span><span class='Modifier'>Λ</span></code> </td><td> <code><span class='Function'>βΎ</span></code></td> </tr> <tr><td> <code><span class='Function'>β</span></code> </td><td> <code><span class='Function'>></span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Function'>β</span></code> </td><td> <code><span class='Function'><</span><span class='Modifier'>Λ</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> -<tr><td> <code><span class='Value'>β</span></code> </td><td> <code><span class='Function'><</span></code> </td><td> <code><span class='Function'>+</span><span class='Modifier'>`</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code></td> </tr> -<tr><td> <code><span class='Value'>β</span></code> </td><td> <code><span class='Function'><</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'><β‘</span><span class='Paren'>)</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> +<tr><td> <code><span class='Value'>β</span></code> </td><td> <code><span class='Function'><</span></code> </td><td> <code><span class='Function'>+</span><span class='Modifier'>`</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code></td> </tr> +<tr><td> <code><span class='Value'>β</span></code> </td><td> <code><span class='Function'><</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'><β‘</span><span class='Paren'>)</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Function'>β</span></code> </td><td> <code><span class='Brace'>{</span><span class='Number'>0</span><span class='Function'>=β‘</span><span class='Value'>π©:</span><span class='Function'>β₯</span><span class='Value'>π©</span><span class='Separator'>β</span><span class='Function'>βΎβ₯</span><span class='Value'>β</span><span class='Modifier'>Β¨</span><span class='Value'>π©</span><span class='Brace'>}</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Value'>β</span></code> </td><td colspan=2><code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Value'>β</span></code> </td><td> </td><td> <code><span class='Function'>/</span><span class='Modifier'>βΌ</span></code></td> </tr> <tr><td> <code><span class='Value'>β©</span></code> </td><td> </td><td> <code><span class='Function'>β/β£</span></code></td> </tr> -<tr><td> <code><span class='Value'>βͺ</span></code> </td><td> <code><span class='Function'>β·</span></code> </td><td> <code><span class='Function'>β£βΎβ</span><span class='Modifier'>Λ</span><span class='Function'>Β¬</span><span class='Composition'>βΈ</span><span class='Function'>/β’</span></code></td> </tr> +<tr><td> <code><span class='Value'>βͺ</span></code> </td><td> <code><span class='Function'>β·</span></code> </td><td> <code><span class='Function'>β£βΎβ</span><span class='Modifier'>Λ</span><span class='Function'>Β¬</span><span class='Modifier2'>βΈ</span><span class='Function'>/β’</span></code></td> </tr> <tr><td> <code><span class='Value'>β³</span></code> </td><td> <code><span class='Function'>β</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Value'>βΈ</span></code> </td><td> <code><span class='Function'>/</span></code> </td><td> <code><span class='Function'>β</span></code></td> </tr> <tr><td> <code><span class='Function'>β</span></code> </td><td> <code><span class='Function'>β</span></code> </td><td> Give up </td> </tr> @@ -172,9 +172,9 @@ <tr><td> <code><span class='Function'>β’</span></code> </td><td> <code><span class='Function'>β </span></code> </td><td> <code><span class='Function'>β’</span></code></td> </tr> <tr><td> <code><span class='Value'>β</span></code> </td><td colspan=2 rowspan=2>To be decided</td> </tr> <tr><td> <code><span class='Value'>β</span></code> </td> </tr> -<tr><td> <code><span class='Value'>β₯</span></code> </td><td> </td><td> <code><span class='Brace'>{</span><span class='Function'>+</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Composition'>βΈ</span><span class='Function'>Γ</span><span class='Paren'>)</span><span class='Modifier'>Β΄</span><span class='Function'>β½</span><span class='Value'>π©</span><span class='Brace'>}</span></code> </td> </tr> -<tr><td> <code><span class='Value'>β€</span></code> </td><td> </td><td> <code><span class='Brace'>{</span><span class='Value'>π¨</span><span class='Function'>|</span><span class='Number'>1</span><span class='Function'>ββ</span><span class='Composition'>β</span><span class='Function'>Γ·</span><span class='Modifier'>`</span><span class='Composition'>βΎ</span><span class='Function'>β½</span><span class='Value'>π¨</span><span class='Function'>βΎ<</span><span class='Value'>π©</span><span class='Brace'>}</span></code></td> </tr> -<tr><td> <code><span class='Value'>βΉ</span></code> </td><td colspan=2><code><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Composition'>β</span><span class='Function'>Γ</span><span class='Composition'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span><span class='Modifier'>βΌ</span></code> I guess</td> </tr> +<tr><td> <code><span class='Value'>β₯</span></code> </td><td> </td><td> <code><span class='Brace'>{</span><span class='Function'>+</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Modifier2'>βΈ</span><span class='Function'>Γ</span><span class='Paren'>)</span><span class='Modifier'>Β΄</span><span class='Function'>β½</span><span class='Value'>π©</span><span class='Brace'>}</span></code> </td> </tr> +<tr><td> <code><span class='Value'>β€</span></code> </td><td> </td><td> <code><span class='Brace'>{</span><span class='Value'>π¨</span><span class='Function'>|</span><span class='Number'>1</span><span class='Function'>ββ</span><span class='Modifier2'>β</span><span class='Function'>Γ·</span><span class='Modifier'>`</span><span class='Modifier2'>βΎ</span><span class='Function'>β½</span><span class='Value'>π¨</span><span class='Function'>βΎ<</span><span class='Value'>π©</span><span class='Brace'>}</span></code></td> </tr> +<tr><td> <code><span class='Value'>βΉ</span></code> </td><td colspan=2><code><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Modifier2'>β</span><span class='Function'>Γ</span><span class='Modifier2'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span><span class='Modifier'>βΌ</span></code> I guess</td> </tr> <tr><td> <code><span class='Value'>β·</span></code> </td><td> N/A </td><td> <code><span class='Function'>β</span></code></td> </tr> </table> @@ -185,18 +185,18 @@ <tr><td> <code><span class='Value'>β</span></code> </td><td colspan=2> <code><span class='Function'>β</span></code> or <code><span class='Modifier'>`</span></code> </td></tr> <tr><td> <code><span class='Modifier'>Β¨</span></code> </td><td colspan=2> <code><span class='Modifier'>Β¨</span></code> </td></tr> <tr><td> <code><span class='Value'>β¨</span></code> </td><td colspan=2> <code><span class='Modifier'>Λ</span></code> </td></tr> -<tr><td> <code><span class='Value'>β£</span></code> </td><td colspan=2> <code><span class='Composition'>β</span></code> </td></tr> -<tr><td> <code><span class='Value'>f.g</span></code> </td><td> </td><td> <code><span class='Value'>f</span><span class='Modifier'>Β΄</span><span class='Composition'>β</span><span class='Value'>g</span><span class='Composition'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span></code> </td></tr> -<tr><td> <code><span class='Composition'>β</span><span class='Number'>.f</span></code> </td><td> </td><td> <code><span class='Value'>f</span><span class='Modifier'>β</span></code> </td></tr> -<tr><td> <code><span class='Function'>A</span><span class='Composition'>β</span><span class='Value'>g</span></code> </td><td> <code><span class='Function'>A</span><span class='Composition'>βΈ</span><span class='Value'>g</span></code> </td><td> </td></tr> -<tr><td> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Function'>B</span></code> </td><td> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Function'>B</span></code> </td><td> </td></tr> -<tr><td> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Value'>g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Value'>g</span></code> </td></tr> -<tr><td> <code><span class='Value'>fβ€</span><span class='Function'>B</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Function'>B</span></code> </td></tr> -<tr><td> <code><span class='Value'>fβ€g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Value'>g</span></code> </td></tr> -<tr><td> <code><span class='Value'>fβ₯g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Composition'>β</span><span class='Value'>g</span></code> </td></tr> -<tr><td> <code><span class='Value'>f@v</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Composition'>βΎ</span><span class='Paren'>(</span><span class='Value'>v</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span></code> </td></tr> +<tr><td> <code><span class='Value'>β£</span></code> </td><td colspan=2> <code><span class='Modifier2'>β</span></code> </td></tr> +<tr><td> <code><span class='Value'>f.g</span></code> </td><td> </td><td> <code><span class='Value'>f</span><span class='Modifier'>Β΄</span><span class='Modifier2'>β</span><span class='Value'>g</span><span class='Modifier2'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span></code> </td></tr> +<tr><td> <code><span class='Modifier2'>β</span><span class='Number'>.f</span></code> </td><td> </td><td> <code><span class='Value'>f</span><span class='Modifier'>β</span></code> </td></tr> +<tr><td> <code><span class='Function'>A</span><span class='Modifier2'>β</span><span class='Value'>g</span></code> </td><td> <code><span class='Function'>A</span><span class='Modifier2'>βΈ</span><span class='Value'>g</span></code> </td><td> </td></tr> +<tr><td> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Function'>B</span></code> </td><td> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Function'>B</span></code> </td><td> </td></tr> +<tr><td> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Value'>g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Value'>g</span></code> </td></tr> +<tr><td> <code><span class='Value'>fβ€</span><span class='Function'>B</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Function'>B</span></code> </td></tr> +<tr><td> <code><span class='Value'>fβ€g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Value'>g</span></code> </td></tr> +<tr><td> <code><span class='Value'>fβ₯g</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Modifier2'>β</span><span class='Value'>g</span></code> </td></tr> +<tr><td> <code><span class='Value'>f@v</span></code> </td><td colspan=2> <code><span class='Value'>f</span><span class='Modifier2'>βΎ</span><span class='Paren'>(</span><span class='Value'>v</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span></code> </td></tr> <tr><td> <code><span class='Value'>fβ </span><span class='Function'>B</span></code> </td><td colspan=2> Uh </td></tr> -<tr><td> <code><span class='Value'>fβΈ</span></code> </td><td><code><span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>βββ</span><span class='Composition'>β</span><span class='Function'>β </span></code></td><td><code><span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code> </td></tr> +<tr><td> <code><span class='Value'>fβΈ</span></code> </td><td><code><span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>βββ</span><span class='Modifier2'>β</span><span class='Function'>β </span></code></td><td><code><span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code> </td></tr> <tr><td> <code><span class='Value'>fβΊ</span><span class='Function'>B</span></code> </td><td colspan=2> <code><span class='Function'>β</span></code> </td></tr> <tr><td> <code><span class='Function'>A</span><span class='Value'>βΆ</span></code> </td><td colspan=2> <code><span class='Value'>β’</span></code> </td></tr> <tr><td> <code><span class='Value'>f&</span></code> </td><td colspan=2> Nothing yet </td></tr> diff --git a/docs/doc/functional.html b/docs/doc/functional.html index e070e279..bc1b7c73 100644 --- a/docs/doc/functional.html +++ b/docs/doc/functional.html @@ -2,7 +2,7 @@ <h1 id="functional-programming">Functional programming</h1> <p>BQN boasts of its functional capabilities, including first-class functions. What sort of functional support does it have, and how can a BQN programmer exercise these and out themself as a Schemer at heart?</p> <p>First, let's be clear about what the terms we're using mean. A language has <em>first-class functions</em> when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments.</p> -<p>Traditionally APL has worked around its lack of first-class functions with operators or second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class.</p> +<p>Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class.</p> <p>The term <em>functional programming</em> is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called <em>first-class functional programming</em>, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, two other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term <em>function-level programming</em> for this usage. A newer usage, which I call <em>pure functional programming</em>, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, <em>typed functional programming</em> is closely associated with pure functional programming and refers to statically-typed functional languages such as Haskell, F#, and Idris (the last of which even supports <em>dependently-typed functional programming</em>, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it is dynamically and not statically typed.</p> <p>Another topic we are interested in is <em>lexical scoping</em> and <em>closures</em>. Lexical scoping means that the realm in which a variable exists is determined by its containing context (in BQN, the surrounding set of curly braces <code><span class='Brace'>{}</span></code>, if any) within the source code. A closure is really an implementation mechanism, but it's often used to refer to a property of lexical scoping that appears when functions defined in a particular block can be accessed after the block finishes execution. For example, they might be returned from a function or assigned to a variable outside of that function's scope. In this case the functions can still access variables in the original scope. I consider this property to be a requirement for a correct lexical scoping implementation, but it's traditionally not a part of APL: implementation might not have lexical scoping (for example, J and I believe A+ use static scoping where functions can't access variables in containing scopes) or might cut off the scope once execution ends, leading to value errors that one wouldn't predict from the rules of lexical scoping.</p> <h2 id="functions-in-apl">Functions in APL</h2> @@ -13,23 +13,23 @@ <p><em>Reminder: I am discussing only first-class functional programming here, and not other concepts like pure or typed functional programming!</em></p> <p>What does functional programming in BQN look like? How is it different from the typical APL style of manipulating functions with operators?</p> <h3 id="working-with-roles">Working with roles</h3> -<p>First, let's look at the basics: a small program that takes a function as its argument and result. The function <code><span class='Function'>Lin</span></code> below gives a linear approximation to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, <code><span class='Function'>π</span></code>.</p> +<p>First, let's look at the basics: a small program that has functions as its argument and result. The function <code><span class='Function'>Lin</span></code> below gives a linear approximation to its function argument based on the values at 0 and 1. To find these two values, we call the argument as a function by using its uppercase spelling, <code><span class='Function'>π</span></code>.</p> <pre><span class='Function'>Lin</span> <span class='Gets'>β</span> <span class='Brace'>{</span> <span class='Value'>v0</span> <span class='Gets'>β</span> <span class='Function'>π</span> <span class='Number'>0</span> <span class='Value'>v0</span> <span class='Function'>+</span> <span class='Paren'>((</span><span class='Function'>π</span> <span class='Number'>1</span><span class='Paren'>)</span> <span class='Function'>-</span> <span class='Value'>v0</span><span class='Paren'>)</span> <span class='Function'>Γ</span> <span class='Function'>β’</span> <span class='Brace'>}</span> </pre> -<p>We can pass it the exponential function as an argument by giving it the name <code><span class='Function'>Exp</span></code> and then referring to it in lowercase (that is, in a value role). The result is a train that adds 1 to <em>e</em>-1 times the argument.</p> +<p>We can pass it the exponential function as an argument by giving it the name <code><span class='Function'>Exp</span></code> and then referring to it in lowercase (that is, in a subject role). The result is a train that adds 1 to <em>e</em>-1 times the argument.</p> <pre> <span class='Function'>Exp</span> <span class='Gets'>β</span> <span class='Function'>β</span> <span class='Function'>Lin</span> <span class='Value'>exp</span> <span class='Paren'>(</span><span class='Number'>1</span> <span class='Function'>+</span> <span class='Paren'>(</span><span class='Number'>1.71828182845905</span> <span class='Function'>Γ</span> <span class='Function'>β’</span><span class='Paren'>))</span> </pre> -<p>As with all functions, the result of <code><span class='Function'>Lin</span></code> has a value role. To use it as a function, we give it a name and then use that name with an uppercase spelling.</p> +<p>As with all functions, the result of <code><span class='Function'>Lin</span></code> has a subject role. To use it as a function, we give it a name and then use that name with an uppercase spelling.</p> <pre> <span class='Value'>expLin</span> <span class='Gets'>β</span> <span class='Function'>Lin</span> <span class='Value'>exp</span> <span class='Function'>ExpLin</span> <span class='Number'>5</span> <span class='Number'>9.59140914229523</span> </pre> -<p>A tricker but more compact method is to use the modifier <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code>, as the input to a modifier can have a value or function role but its output always has a function role.</p> +<p>A tricker but more compact method is to use the 1-modifier <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code>, as the input to a modifier can have a subject or function role but its output always has a function role.</p> <pre> <span class='Paren'>(</span><span class='Function'>Lin</span> <span class='Value'>exp</span><span class='Paren'>)</span><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span> <span class='Number'>5</span> <span class='Number'>9.59140914229523</span> </pre> @@ -48,27 +48,27 @@ <span class='Number'>9.59140914229523</span> </pre> <h3 id="arrays-of-functions">Arrays of functions</h3> -<p>It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as values. Here's an example of an array of functions with a reduction applied to it, composing them together.</p> -<pre> <span class='Brace'>{</span><span class='Function'>π</span><span class='Composition'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span> <span class='Function'>β</span><span class='Ligature'>βΏ</span><span class='Function'>-</span><span class='Ligature'>βΏ</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>)</span> -<span class='Function'>β</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>-</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>))</span> +<p>It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a reduction applied to it, composing them together.</p> +<pre> <span class='Brace'>{</span><span class='Function'>π</span><span class='Modifier2'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span> <span class='Function'>β</span><span class='Ligature'>βΏ</span><span class='Function'>-</span><span class='Ligature'>βΏ</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>)</span> +<span class='Function'>β</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>-</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>))</span> </pre> -<p>Like any function, this one can be given a name and then called. A quirk of this way of defining a function is that it has a value role (it's the result of the function <code><span class='Brace'>{</span><span class='Function'>π</span><span class='Composition'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span></code>) and so must be defined with a lowercase name.</p> -<pre> <span class='Value'>gauss</span> <span class='Gets'>β</span> <span class='Brace'>{</span><span class='Function'>π</span><span class='Composition'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span> <span class='Function'>β</span><span class='Ligature'>βΏ</span><span class='Function'>-</span><span class='Ligature'>βΏ</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>)</span> +<p>Like any function, this one can be given a name and then called. A quirk of this way of defining a function is that it has a subject role (it's the result of the function <code><span class='Brace'>{</span><span class='Function'>π</span><span class='Modifier2'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span></code>) and so must be defined with a lowercase name.</p> +<pre> <span class='Value'>gauss</span> <span class='Gets'>β</span> <span class='Brace'>{</span><span class='Function'>π</span><span class='Modifier2'>β</span><span class='Function'>π</span><span class='Brace'>}</span><span class='Modifier'>Β΄</span> <span class='Function'>β</span><span class='Ligature'>βΏ</span><span class='Function'>-</span><span class='Ligature'>βΏ</span><span class='Paren'>(</span><span class='Function'>Γ</span><span class='Modifier'>Λ</span><span class='Paren'>)</span> <span class='Function'>Gauss</span> <span class='Number'>2</span> <span class='Number'>0.0183156388887342</span> </pre> <p>Another, and probably more common, use of arrays of functions is to apply several different functions to one or more arguments. Here we apply three different functions to the number 9:</p> -<pre> <span class='Bracket'>β¨</span><span class='Function'>β</span><span class='Separator'>,</span> <span class='Number'>2</span><span class='Composition'>βΈ</span><span class='Function'>βΎ</span><span class='Separator'>,</span> <span class='Function'>β’-β</span><span class='Bracket'>β©</span> <span class='Brace'>{</span><span class='Function'>π</span><span class='Value'>π©</span><span class='Brace'>}</span><span class='Modifier'>Β¨</span> <span class='Number'>9</span> +<pre> <span class='Bracket'>β¨</span><span class='Function'>β</span><span class='Separator'>,</span> <span class='Number'>2</span><span class='Modifier2'>βΈ</span><span class='Function'>βΎ</span><span class='Separator'>,</span> <span class='Function'>β’-β</span><span class='Bracket'>β©</span> <span class='Brace'>{</span><span class='Function'>π</span><span class='Value'>π©</span><span class='Brace'>}</span><span class='Modifier'>Β¨</span> <span class='Number'>9</span> <span class='Value'>[</span> <span class='Number'>3</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>9</span> <span class='Value'>]</span> <span class='Number'>Β―8094.083927575384</span> <span class='Value'>]</span> </pre> -<p>The composition Choose (<code><span class='Composition'>βΆ</span></code>) relies on arrays of functions toβ¦ function. It's very closely related to Pick <code><span class='Function'>β</span></code>, and in fact when the left operand and the elements of the right operand are all value types there's no real difference: Choose returns the constant function <code><span class='Value'>π</span><span class='Function'>β</span><span class='Value'>π</span></code>.</p> -<pre> <span class='Number'>2</span><span class='Composition'>βΆ</span><span class='String'>"abcdef"</span> <span class='String'>"arg"</span> +<p>The 2-modifier Choose (<code><span class='Modifier2'>βΆ</span></code>) relies on arrays of functions toβ¦ function. It's very closely related to Pick <code><span class='Function'>β</span></code>, and in fact when the left operand and the elements of the right operand are all data there's no real difference: Choose returns the constant function <code><span class='Value'>π</span><span class='Function'>β</span><span class='Value'>π</span></code>.</p> +<pre> <span class='Number'>2</span><span class='Modifier2'>βΆ</span><span class='String'>"abcdef"</span><span class='Ligature'>βΏ</span><span class='String'>"arg"</span> <span class='Value'>c</span> </pre> <p>When the operands contain functions, however, the potential of Choose as a ternary-or-more operator opens up. Here's a function for a step in the Collatz sequence, which halves an even input but multiplies an odd input by 3 and adds 1. To get the sequence for a number, we can apply the same function many times. It's an open problem whether the sequence always ends with the repetition 4, 2, 1, but it can take a surprisingly long time to get thereβtry 27 as an argument.</p> -<pre> <span class='Paren'>(</span><span class='Number'>2</span><span class='Composition'>βΈ</span><span class='Function'>|</span><span class='Paren'>)</span><span class='Composition'>βΆ</span><span class='Bracket'>β¨</span><span class='Function'>Γ·</span><span class='Composition'>β</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>3</span><span class='Function'>Γβ’</span><span class='Bracket'>β©</span><span class='Modifier'>Β¨</span> <span class='Number'>6</span><span class='Ligature'>βΏ</span><span class='Number'>7</span> +<pre> <span class='Paren'>(</span><span class='Number'>2</span><span class='Modifier2'>βΈ</span><span class='Function'>|</span><span class='Paren'>)</span><span class='Modifier2'>βΆ</span><span class='Bracket'>β¨</span><span class='Function'>Γ·</span><span class='Modifier2'>β</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>3</span><span class='Function'>Γβ’</span><span class='Bracket'>β©</span><span class='Modifier'>Β¨</span> <span class='Number'>6</span><span class='Ligature'>βΏ</span><span class='Number'>7</span> <span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>22</span> <span class='Value'>]</span> - <span class='Paren'>(</span><span class='Number'>2</span><span class='Composition'>βΈ</span><span class='Function'>|</span><span class='Paren'>)</span><span class='Composition'>βΆ</span><span class='Bracket'>β¨</span><span class='Function'>Γ·</span><span class='Composition'>β</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>3</span><span class='Function'>Γβ’</span><span class='Bracket'>β©</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>β</span><span class='Number'>10</span><span class='Paren'>)</span> <span class='Number'>6</span> + <span class='Paren'>(</span><span class='Number'>2</span><span class='Modifier2'>βΈ</span><span class='Function'>|</span><span class='Paren'>)</span><span class='Modifier2'>βΆ</span><span class='Bracket'>β¨</span><span class='Function'>Γ·</span><span class='Modifier2'>β</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Function'>+</span><span class='Number'>3</span><span class='Function'>Γβ’</span><span class='Bracket'>β©</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>β</span><span class='Number'>10</span><span class='Paren'>)</span> <span class='Number'>6</span> <span class='Value'>[</span> <span class='Number'>6</span> <span class='Number'>3</span> <span class='Number'>10</span> <span class='Number'>5</span> <span class='Number'>16</span> <span class='Number'>8</span> <span class='Number'>4</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Number'>4</span> <span class='Value'>]</span> </pre> diff --git a/docs/doc/group.html b/docs/doc/group.html index 052aea7b..aa96a12d 100644 --- a/docs/doc/group.html +++ b/docs/doc/group.html @@ -2,11 +2,11 @@ <h1 id="group">Group</h1> <p>BQN replaces the <a href="https://aplwiki.com/wiki/Key">Key</a> operator from J or Dyalog APL, and <a href="https://aplwiki.com/wiki/Partition_representations">many forms of partitioning</a>, with a single (ambivalent) Group function <code><span class='Function'>β</span></code>. This function is somewhat related to the K function <code><span class='Function'>=</span></code> of the same name, but results in an array rather than a dictionary.</p> <p>The BQN prototype does not implement this function: instead it uses <code><span class='Function'>β</span></code> for a Group/Key function very similar to <code><span class='Brace'>{</span><span class='Value'>ββ΅</span><span class='Brace'>}</span><span class='Value'>βΈ</span></code> in Dyalog APL, and also has a Cut function <code><span class='Value'>\</span></code>. The new BQN Group on numeric arguments (equivalently, rank-1 results) can be defined like this:</p> -<pre><span class='Function'>β</span><span class='Gets'>β©</span><span class='Paren'>((</span><span class='Function'>β</span><span class='Number'>1</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Function'>>β</span><span class='Modifier'>Β΄</span><span class='Paren'>))</span><span class='Function'>=</span><span class='Modifier'>Β¨</span><span class='Function'><</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>β£</span> <span class='Function'>/</span><span class='Modifier'>Β¨</span><span class='Composition'>β</span><span class='Function'><</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Function'>β </span><span class='Value'>β </span><span class='Function'>β’</span> +<pre><span class='Function'>β</span><span class='Gets'>β©</span><span class='Paren'>((</span><span class='Function'>β</span><span class='Number'>1</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Function'>>β</span><span class='Modifier'>Β΄</span><span class='Paren'>))</span><span class='Function'>=</span><span class='Modifier'>Β¨</span><span class='Function'><</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>β£</span> <span class='Function'>/</span><span class='Modifier'>Β¨</span><span class='Modifier2'>β</span><span class='Function'><</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'>β </span><span class='Value'>β </span><span class='Function'>β’</span> </pre> -<p>Once defined, the old BQN Key (dyadic) is <code><span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code> and Group (monadic) is <code><span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>βββ</span><span class='Composition'>β</span><span class='Function'>β </span></code> using the Deduplicate or Unique Cells function <code><span class='Function'>β·</span></code> (BQN2NGN spells it <code><span class='Value'>βͺ</span></code>). Cut on matching-length arguments is <code><span class='Function'>+</span><span class='Modifier'>`</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code>.</p> +<p>Once defined, the old BQN Key (dyadic) is <code><span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code> and Group (monadic) is <code><span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>βββ</span><span class='Modifier2'>β</span><span class='Function'>β </span></code> using the Deduplicate or Unique Cells function <code><span class='Function'>β·</span></code> (BQN2NGN spells it <code><span class='Value'>βͺ</span></code>). Cut on matching-length arguments is <code><span class='Function'>+</span><span class='Modifier'>`</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>.</p> <h2 id="definition">Definition</h2> -<p>Group operates on a numeric list of indices and a value array, treated as a list of its major cells, to produce a list of groups, each of which is a selection from the values. The indices and values have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the value should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result.</p> +<p>Group operates on a numeric list of indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result.</p> <pre> <span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span> <span class='Function'>β</span> <span class='String'>"abcde"</span> <span class='Comment'># Corresponding indices and values </span><span class='Value'>β</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>2</span> <span class='Number'>0</span> <span class='Number'>1</span> @@ -15,9 +15,9 @@ <span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span> <span class='Function'>β</span> <span class='String'>"abcde"</span> <span class='Comment'># Values grouped by index </span><span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>ad</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>be</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>c</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> -<p>For example, we might choose to group a list of words by length. Within each group, values maintain the ordering they had in the list originally.</p> +<p>For example, we might choose to group a list of words by length. Within each group, cells maintain the ordering they had in the list originally.</p> <pre> <span class='Value'>phrase</span> <span class='Gets'>β</span> <span class='String'>"BQN"</span><span class='Ligature'>βΏ</span><span class='String'>"uses"</span><span class='Ligature'>βΏ</span><span class='String'>"notation"</span><span class='Ligature'>βΏ</span><span class='String'>"as"</span><span class='Ligature'>βΏ</span><span class='String'>"a"</span><span class='Ligature'>βΏ</span><span class='String'>"tool"</span><span class='Ligature'>βΏ</span><span class='String'>"of"</span><span class='Ligature'>βΏ</span><span class='String'>"thought"</span> - <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Function'>β </span><span class='Modifier'>Β¨</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Value'>phrase</span> + <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Function'>β </span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Value'>phrase</span> <span class='Value'>β</span> <span class='Value'>[]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Value'>]</span> <span class='Value'>]</span> @@ -32,9 +32,9 @@ </pre> <p>(Could we define <code><span class='Value'>phrase</span></code> more easily? See <a href="#partitioning">below</a>.)</p> <p>If we'd like to ignore words of 0 letters, or more than 5, we can set all word lengths greater than 5 to 0, then reduce the lengths by 1. Two words end up with left argument values of Β―1 and are omitted from the result.</p> -<pre> <span class='Number'>1</span><span class='Function'>-</span><span class='Modifier'>Λ</span><span class='Function'>β€</span><span class='Composition'>β</span><span class='Number'>5</span><span class='Composition'>βΈ</span><span class='Function'>Γβ </span><span class='Modifier'>Β¨</span><span class='Value'>phrase</span> +<pre> <span class='Number'>1</span> <span class='Function'>-</span><span class='Modifier'>Λ</span> <span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Number'>5</span><span class='Modifier2'>βΈ</span><span class='Function'>Γ</span> <span class='Function'>β </span><span class='Modifier'>Β¨</span> <span class='Value'>phrase</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>Β―1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>3</span> <span class='Number'>1</span> <span class='Number'>Β―1</span> <span class='Value'>]</span> - <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Brace'>{</span><span class='Number'>1</span><span class='Function'>-</span><span class='Modifier'>Λ</span><span class='Function'>β€</span><span class='Composition'>β</span><span class='Number'>5</span><span class='Composition'>βΈ</span><span class='Function'>Γβ </span><span class='Modifier'>Β¨</span><span class='Value'>π©</span><span class='Brace'>}</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Value'>phrase</span> + <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Brace'>{</span><span class='Number'>1</span><span class='Function'>-</span><span class='Modifier'>Λ</span><span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Number'>5</span><span class='Modifier2'>βΈ</span><span class='Function'>Γβ </span><span class='Modifier'>Β¨</span><span class='Value'>π©</span><span class='Brace'>}</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Value'>phrase</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>as</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>of</span> <span class='Value'>]</span> <span class='Value'>]</span> @@ -43,7 +43,7 @@ <span class='Value'>β</span> </pre> <p>Note that the length of the result is determined by the largest index. So the result never includes trailing empty groups. A reader of the above code might expect 5 groups (lengths 1 through 5), but there are no words of length 5, so the last group isn't there.</p> -<p>When Group is called dyadically, the left argument is used for the indices and the right is used for values, as seen above. When it is called monadically, the right argument gives the indices and the values grouped are the right argument's indices, that is, <code><span class='Function'>ββ </span><span class='Value'>π©</span></code>.</p> +<p>When Group is called dyadically, the left argument is used for the indices and the right is used for values, as seen above. When it is called monadically, the right argument, which must be a list, gives the indices and the values grouped are the right argument's indices, that is, <code><span class='Function'>ββ </span><span class='Value'>π©</span></code>.</p> <pre> <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Function'>β</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>Β―1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Value'>β</span> <span class='Value'>[]</span> @@ -54,7 +54,7 @@ </pre> <p>Here, the index 2 appears at indices 0 and 3 while the index 3 appears at index 1.</p> <h3 id="multidimensional-grouping">Multidimensional grouping</h3> -<p>Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a vector of numeric vectors, and the result has rank <code><span class='Function'>β </span><span class='Value'>π¨</span></code> while its elementsβas alwaysβhave the same rank as <code><span class='Value'>π©</span></code>. The result shape is <code><span class='Number'>1</span><span class='Function'>+β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span></code>, while the shape of element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is <code><span class='Value'>i</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Composition'>β</span><span class='Function'>=</span><span class='Modifier'>Β¨</span><span class='Value'>π¨</span></code>. If every element of <code><span class='Value'>π¨</span></code> is sorted ascending and contains only non-negative numbers, we have <code><span class='Value'>π©</span><span class='Function'>β‘βΎ</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code>, that is, Join is the inverse of Partition.</p> +<p>Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank <code><span class='Function'>β </span><span class='Value'>π¨</span></code> while its elementsβas alwaysβhave the same rank as <code><span class='Value'>π©</span></code>. The result shape is <code><span class='Number'>1</span><span class='Function'>+β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span></code>, while the shape of element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is <code><span class='Value'>i</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Modifier2'>β</span><span class='Function'>=</span><span class='Modifier'>Β¨</span><span class='Value'>π¨</span></code>. If every element of <code><span class='Value'>π¨</span></code> is sorted ascending and contains only non-negative numbers, we have <code><span class='Value'>π©</span><span class='Function'>β‘βΎ</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code>, that is, Join is the inverse of Partition.</p> <p>Here we split up a rank-2 array into a rank-2 array of rank-2 arrays. Along the first axis we simply separate the first pair and second pair of rowsβa partition. Along the second axis we separate odd from even indices.</p> <pre> <span class='Bracket'>β¨</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Bracket'>β©</span><span class='Function'>β</span><span class='Paren'>(</span><span class='Number'>10</span><span class='Function'>Γβ</span><span class='Number'>4</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Modifier'>β</span><span class='Function'>β</span><span class='Number'>7</span> <span class='Value'>β</span> @@ -68,8 +68,8 @@ <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> </pre> -<p>Each group <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is composed of the cells <code><span class='Value'>j</span><span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Value'>π©</span></code> such that <code><span class='Value'>i</span><span class='Function'>β’</span><span class='Value'>j</span><span class='Function'>β</span><span class='Modifier'>Β¨</span><span class='Value'>π¨</span></code>. The groups retain their array structure and ordering along each argument axis. Using multidimensional Replicate we can say that <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is <code><span class='Paren'>(</span><span class='Value'>i</span><span class='Function'>=</span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Function'>/</span><span class='Value'>π©</span></code>.</p> -<p>The monadic case works similarly: Group Indices always satisfies <code><span class='Function'>β</span><span class='Value'>π©</span> <span class='Gets'>ββ</span> <span class='Value'>π©</span><span class='Function'>βββ </span><span class='Composition'>β</span><span class='Number'>1</span> <span class='Value'>x</span></code>. As with <code><span class='Function'>β</span></code>, the depth of the result of Group Indices is always one greater than that of its argument. A depth-0 argument is not allowed.</p> +<p>Each group <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is composed of the cells <code><span class='Value'>j</span><span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Value'>π©</span></code> such that <code><span class='Value'>i</span><span class='Function'>β’</span><span class='Value'>j</span><span class='Function'>β</span><span class='Modifier'>Β¨</span><span class='Value'>π¨</span></code>. The groups retain their array structure and ordering along each argument axis. Using multidimensional Replicate we can say that <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>π©</span></code> is <code><span class='Paren'>(</span><span class='Value'>i</span><span class='Function'>=</span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Function'>/</span><span class='Value'>π©</span></code>.</p> +<p>The monadic case works similarly: Group Indices always satisfies <code><span class='Function'>β</span><span class='Value'>π©</span> <span class='Gets'>ββ</span> <span class='Value'>π©</span><span class='Function'>βββ </span><span class='Modifier2'>β</span><span class='Number'>1</span><span class='Value'>π©</span></code>. As with <code><span class='Function'>β</span></code>, the depth of the result of Group Indices is always one greater than that of its argument. A depth-0 argument is not allowed.</p> <h2 id="properties">Properties</h2> <p>Group is closely related to the inverse of Indices, <code><span class='Function'>/</span><span class='Modifier'>βΌ</span></code>. In fact, inverse Indices called on the index argument gives the length of each group:</p> <pre> <span class='Function'>β </span><span class='Modifier'>Β¨</span><span class='Function'>β</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> @@ -84,15 +84,15 @@ <p>Called dyadically, Group sorts the right argument according to the left and adds some extra structure. If this structure is removed with Join, Group can be thought of as a kind of sorting.</p> <pre> <span class='Function'>βΎ</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>Β―1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Function'>β</span> <span class='String'>"abcde"</span> <span class='Value'>[</span> <span class='Value'>caeb</span> <span class='Value'>]</span> - <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>Β―1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Brace'>{</span><span class='Function'>F</span><span class='Gets'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>β€</span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>/</span> <span class='Separator'>β</span> <span class='Value'>π¨</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>β</span><span class='Function'>F</span><span class='Value'>π©</span><span class='Brace'>}</span> <span class='String'>"abcde"</span> + <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>Β―1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Brace'>{</span><span class='Function'>F</span><span class='Gets'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>β€</span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>/</span> <span class='Separator'>β</span> <span class='Value'>π¨</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'>F</span><span class='Value'>π©</span><span class='Brace'>}</span> <span class='String'>"abcde"</span> <span class='Value'>[</span> <span class='Value'>caeb</span> <span class='Value'>]</span> </pre> <p>Group can even be implemented with the same techniques as a bucket sort, which can be branchless and fast.</p> <h2 id="applications">Applications</h2> -<p>The obvious application of Group is to group some values according to a known or computed property. If this property isn't an integer, it can be turned into one using Unique and Index Of (the combination <code><span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code> has been called "self-classify").</p> +<p>The obvious application of Group is to group some values according to a known or computed property. If this property isn't an integer, it can be turned into one using Unique and Index Of (the combination <code><span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code> has been called "self-classify").</p> <pre> <span class='Value'>ln</span> <span class='Gets'>β</span> <span class='String'>"Phelps"</span><span class='Ligature'>βΏ</span><span class='String'>"Latynina"</span><span class='Ligature'>βΏ</span><span class='String'>"BjΓΈrgen"</span><span class='Ligature'>βΏ</span><span class='String'>"Andrianov"</span><span class='Ligature'>βΏ</span><span class='String'>"BjΓΈrndalen"</span> <span class='Value'>co</span> <span class='Gets'>β</span> <span class='String'>"US"</span> <span class='Ligature'>βΏ</span><span class='String'>"SU"</span> <span class='Ligature'>βΏ</span><span class='String'>"NO"</span> <span class='Ligature'>βΏ</span><span class='String'>"SU"</span> <span class='Ligature'>βΏ</span><span class='String'>"NO"</span> - <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Function'>β·</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Value'>ln</span> + <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Function'>β·</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Value'>ln</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Function'>Phelps</span> <span class='Value'>]</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Function'>Latynina</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Function'>Andrianov</span> <span class='Value'>]</span> <span class='Value'>]</span> @@ -101,7 +101,7 @@ </pre> <p>If we would like a particular index to key correspondence, we can use a fixed left argument to Index Of.</p> <pre> <span class='Value'>countries</span> <span class='Gets'>β</span> <span class='String'>"IT"</span><span class='Ligature'>βΏ</span><span class='String'>"JP"</span><span class='Ligature'>βΏ</span><span class='String'>"NO"</span><span class='Ligature'>βΏ</span><span class='String'>"SU"</span><span class='Ligature'>βΏ</span><span class='String'>"US"</span> - <span class='Value'>countries</span> <span class='Function'>βΎ</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Value'>countries</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Function'>β</span> <span class='Value'>ln</span> + <span class='Value'>countries</span> <span class='Function'>βΎ</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Value'>countries</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Value'>ln</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Function'>IT</span> <span class='Value'>]</span> <span class='Value'>[]</span> <span class='Value'>[</span> <span class='Function'>JP</span> <span class='Value'>]</span> <span class='Value'>[]</span> @@ -112,7 +112,7 @@ </pre> <p>However, this solution will fail if there are trailing keys with no values. To force the result to have a particular length you can append that length as a dummy index to each argument, then remove the last group after grouping.</p> <pre> <span class='Value'>countries</span> <span class='Gets'>β©</span> <span class='String'>"IT"</span><span class='Ligature'>βΏ</span><span class='String'>"JP"</span><span class='Ligature'>βΏ</span><span class='String'>"NO"</span><span class='Ligature'>βΏ</span><span class='String'>"SU"</span><span class='Ligature'>βΏ</span><span class='String'>"US"</span><span class='Ligature'>βΏ</span><span class='String'>"ZW"</span> - <span class='Value'>countries</span> <span class='Function'>βΎ</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Value'>countries</span><span class='Brace'>{</span><span class='Value'>π</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΈ</span><span class='Paren'>(</span><span class='Number'>Β―1</span><span class='Function'>ββ</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>βΎ</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>β </span><span class='Value'>π</span><span class='Paren'>)))</span><span class='Brace'>}</span> <span class='Value'>ln</span> + <span class='Value'>countries</span> <span class='Function'>βΎ</span><span class='Modifier'>Λ</span> <span class='Value'>co</span> <span class='Value'>countries</span><span class='Brace'>{</span><span class='Value'>π</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΈ</span><span class='Paren'>(</span><span class='Number'>Β―1</span><span class='Function'>ββ</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>βΎ</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>β </span><span class='Value'>π</span><span class='Paren'>)))</span><span class='Brace'>}</span> <span class='Value'>ln</span> <span class='Value'>β</span> <span class='Value'>[</span> <span class='Function'>IT</span> <span class='Value'>]</span> <span class='Value'>[]</span> <span class='Value'>[</span> <span class='Function'>JP</span> <span class='Value'>]</span> <span class='Value'>[]</span> @@ -124,29 +124,29 @@ </pre> <h3 id="partitioning">Partitioning</h3> <p>In examples we have been using a list of strings stranded together. Often it's more convenient to write the string with spaces, and split it up as part of the code. In this case, the index corresponding to each word (that is, each letter in the word) is the number of spaces before it. We can get this number of spaces from a prefix sum on the boolean list which is 1 at each space.</p> -<pre> <span class='String'>' '</span><span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>`</span><span class='Composition'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>"BQN uses notation as a tool of thought"</span> +<pre> <span class='String'>' '</span><span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>`</span><span class='Modifier2'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>"BQN uses notation as a tool of thought"</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Function'>BQN</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>uses</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>notation</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>as</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>tool</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>of</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>thought</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> <p>To avoid including spaces in the result, we should change the result index at each space to Β―1. Here is one way to do that:</p> -<pre> <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>"BQN uses notation as a tool of thought"</span> +<pre> <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>"BQN uses notation as a tool of thought"</span> <span class='Value'>[</span> <span class='Value'>[</span> <span class='Function'>BQN</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>uses</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>notation</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>as</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>a</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>tool</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>of</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>thought</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> -<p>A function with structural Under, such as <code><span class='Brace'>{</span><span class='Number'>Β―1</span><span class='Modifier'>Β¨</span><span class='Composition'>βΎ</span><span class='Paren'>(</span><span class='Value'>π©</span><span class='Composition'>βΈ</span><span class='Function'>/</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Modifier'>`</span><span class='Value'>π©</span><span class='Brace'>}</span></code>, would also work.</p> +<p>A function with structural Under, such as <code><span class='Brace'>{</span><span class='Number'>Β―1</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΎ</span><span class='Paren'>(</span><span class='Value'>π©</span><span class='Modifier2'>βΈ</span><span class='Function'>/</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Modifier'>`</span><span class='Value'>π©</span><span class='Brace'>}</span></code>, would also work.</p> <p>In other cases, we might want to split on spaces, so that words are separated by any number of spaces, and extra spaces don't affect the output. Currently our function makes a new word with each space:</p> -<pre> <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>" string with spaces "</span> +<pre> <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>" string with spaces "</span> <span class='Value'>[</span> <span class='Value'>[]</span> <span class='Value'>[]</span> <span class='Value'>[</span> <span class='Value'>string</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>with</span> <span class='Value'>]</span> <span class='Value'>[]</span> <span class='Value'>[</span> <span class='Value'>spaces</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> <p>However, trailing spaces are ignored because Group never produces trailing empty groups (to get them back we would use a dummy final character in the string). To avoid empty words, we should increase the word index only once per group of spaces. We can do this by taking the prefix sum of a list that is 1 only for a space with no space before it. To make such a list, we can use the <a href="windows.html">Windows</a> function. We will extend our list with an initial 1 so that leading spaces will be ignored. Then we take windows of the same length as the original list: the first includes the dummy argument followed by a shifted copy of the list, and the second is the original list. These represent whether the previous and current characters are spaces; we want positions where the previous wasn't a space and the current is.</p> -<pre> <span class='Function'>β</span><span class='Composition'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>)</span> <span class='String'>' '</span><span class='Function'>=</span><span class='String'>" string with spaces "</span> <span class='Comment'># All, then filtered, spaces +<pre> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>)</span> <span class='String'>' '</span><span class='Function'>=</span><span class='String'>" string with spaces "</span> <span class='Comment'># All, then filtered, spaces </span><span class='Value'>β</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> - <span class='Function'>β</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Composition'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>))</span><span class='String'>' '</span><span class='Function'>=</span><span class='String'>" string with spaces "</span> <span class='Comment'># More processing + <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Modifier2'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>))</span><span class='String'>' '</span><span class='Function'>=</span><span class='String'>" string with spaces "</span> <span class='Comment'># More processing </span><span class='Value'>β</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>Β―1</span> <span class='Number'>Β―1</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>0</span> <span class='Number'>Β―1</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Number'>Β―1</span> <span class='Number'>Β―1</span> <span class='Number'>2</span> <span class='Number'>2</span> <span class='Number'>2</span> <span class='Number'>2</span> <span class='Number'>2</span> <span class='Number'>2</span> <span class='Number'>Β―1</span> <span class='Number'>Β―1</span> <span class='Number'>Β―1</span> <span class='Value'>β</span> - <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Composition'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>))</span><span class='Composition'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>" string with spaces "</span> <span class='Comment'># Final result + <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>β’-</span><span class='Modifier'>Λ</span><span class='Function'>Β¬Γ+</span><span class='Modifier'>`</span><span class='Modifier2'>β</span><span class='Paren'>((</span><span class='Function'><</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Number'>1</span><span class='Function'>βΎβ’</span><span class='Paren'>))</span><span class='Modifier2'>β</span><span class='Function'>=ββ’</span><span class='Paren'>)</span><span class='String'>" string with spaces "</span> <span class='Comment'># Final result </span><span class='Value'>[</span> <span class='Value'>[</span> <span class='Value'>string</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>with</span> <span class='Value'>]</span> <span class='Value'>[</span> <span class='Value'>spaces</span> <span class='Value'>]</span> <span class='Value'>]</span> </pre> diff --git a/docs/doc/index.html b/docs/doc/index.html index 972611f6..a822c0a9 100644 --- a/docs/doc/index.html +++ b/docs/doc/index.html @@ -9,7 +9,7 @@ </ul> <p>Primitives:</p> <ul> -<li><a href="depth.html">Array depth</a> (<code><span class='Function'>β‘</span></code> and <code><span class='Composition'>β</span></code>)</li> +<li><a href="depth.html">Array depth</a> (<code><span class='Function'>β‘</span></code> and <code><span class='Modifier2'>β</span></code>)</li> <li><a href="group.html">Group</a> (<code><span class='Function'>β</span></code>)</li> <li><a href="join.html">Join</a> (<code><span class='Function'>βΎ</span></code>)</li> <li><a href="logic.html">Logical functions</a> (<code><span class='Function'>β§β¨Β¬</span></code>)</li> diff --git a/docs/doc/indices.html b/docs/doc/indices.html index 06f0af0c..68651b13 100644 --- a/docs/doc/indices.html +++ b/docs/doc/indices.html @@ -1,7 +1,7 @@ <head><link href="../style.css" rel="stylesheet"/></head> <h1 id="indices">Indices</h1> -<p>One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the leading axis theory, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0) indices, which can refer to vector elements or array major cells (or more generally indexing along any particular axis). When using this kind of element indices, arrays are required to be vectors. Only two functions allow the use of vector element indices: Range (<code><span class='Function'>β</span></code>), which can accept a vector argument, and Pick (<code><span class='Function'>β</span></code>), which uses the depth-1 arrays in its left argument as index scalars or vectors.</p> -<p>The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. <code><span class='Function'>β</span></code> is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the return type of indices is as useful as possible.</p> +<p>One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the leading axis theory, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using this kind of element index, indexed arrays are required to be lists. Only two functions allow the use of list element indices: Range (<code><span class='Function'>β</span></code>), which can accept a list argument, and Pick (<code><span class='Function'>β</span></code>), which uses the depth-1 arrays in its left argument as index scalars or lists. Others use single-number indices to refer to cells.</p> +<p>The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. <code><span class='Function'>β</span></code> is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the kind of index returned is as useful as possible.</p> <table> <thead> <tr> @@ -16,7 +16,7 @@ <td><code><span class='Function'>β</span></code></td> <td></td> <td></td> -<td>Element scalar or vector</td> +<td>Element scalar or list</td> </tr> <tr> <td><code><span class='Function'>/</span></code></td> @@ -40,7 +40,7 @@ <td></td> <td><code><span class='Function'>β</span></code></td> <td><code><span class='Value'>π¨</span></code></td> -<td>Element vector</td> +<td>Element list</td> </tr> <tr> <td><code><span class='Function'>β</span></code></td> @@ -80,20 +80,20 @@ </tr> </tbody> </table> -<p>Dyadic Transpose (<code><span class='Function'>β</span></code>) takes an index into the right argument axes as its left argument, but since array shape is 1-dimensional, there is only one sensible choice for this, a single number.</p> +<p>Dyadic Transpose (<code><span class='Function'>β</span></code>) uses indices into the right argument axes in its left argument, but since array shape is 1-dimensional, there is only one sensible choice for this, a single number.</p> <h1 id="element-indices">Element indices</h1> -<p>In general, the index of an element of an array is a vector whose length matches the array rank. It is also possible to use a number for an index into a vector, as the vector index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (<code><span class='Function'>β</span></code> in BQN) return a numeric vector when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency.</p> -<p>Functions <code><span class='Function'>β</span></code>, <code><span class='Function'>/</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> naturally deal with element indices. Each of these can be defined to use vector indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts <code><span class='Function'>β</span></code> and monadic <code><span class='Function'>/</span></code> to use depth-0 indices, which comes with the requirement that the arguments to monadic <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, and the result of monadic <code><span class='Function'>β</span></code>, must be vectors. For dyadic <code><span class='Function'>β</span></code> the depth-1 elements of the left argument are vectors of indices along axes of the result; see <a href="group.html#multidimensional-grouping">the documentation</a>. The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on vector indices.</p> -<p>Unlike <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code> and <code><span class='Function'>β</span></code> do use vector element indices. For <code><span class='Function'>β</span></code> this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric vector); if passed a vector, it uses vector indices and the result has depth 2 (the result depth is always one greater than the argument depth). For <code><span class='Function'>β</span></code>, vector indices are chosen because <code><span class='Function'>β</span></code> handles scalar indices well already. When selecting multiple elements from a vector, they would typically have to be placed in an array, which is equivalent to <code><span class='Function'>β</span></code> with a numeric vector left argument. A single scalar index to <code><span class='Function'>β</span></code> is converted to a vector, so it can be used to select a single element if only one is wanted. To select multiple elements, <code><span class='Function'>β</span></code> uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element vector an index, or does it contain an index?</p> +<p>In general, the index of an element of an array is a list whose length matches the array rank. It is also possible to use a number for an index into a list, as the list index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (<code><span class='Function'>β</span></code> in BQN) return a numeric list when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency.</p> +<p>Functions <code><span class='Function'>β</span></code>, <code><span class='Function'>/</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts <code><span class='Function'>β</span></code> and monadic <code><span class='Function'>/</span></code> to use depth-0 indices, which comes with the requirement that the arguments to monadic <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, and the result of monadic <code><span class='Function'>β</span></code>, must be lists. For dyadic <code><span class='Function'>β</span></code> the depth-1 elements of the left argument are lists of indices along axes of the result; see <a href="group.html#multidimensional-grouping">the documentation</a>. The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on list indices.</p> +<p>Unlike <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code> and <code><span class='Function'>β</span></code> do use list element indices. For <code><span class='Function'>β</span></code> this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For <code><span class='Function'>β</span></code>, list indices are chosen because <code><span class='Function'>β</span></code> handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to <code><span class='Function'>β</span></code> with a numeric list left argument. A single scalar index to <code><span class='Function'>β</span></code> is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, <code><span class='Function'>β</span></code> uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index?</p> <h1 id="major-cell-indices">Major cell indices</h1> -<p>One of the successes of the <a href="https://aplwiki.com/wiki/Leading_axis_theory">leading axis model</a> is to introduce a kind of index for multidimensional arrays that is easier to work with than vector indices. The model introduces <a href="https://aplwiki.com/wiki/Cell">cells</a>, where a cell index is a vector of any length up to the containing array's rank. General cell indices are discussed in the next section; first we introduce a special case, indices into major cells or Β―1-cells. These cells naturally form a list, so the index of a major cell is a single number. These indices can also be considered indices along the first axis, since an index along any axis is a single number.</p> +<p>One of the successes of the <a href="https://aplwiki.com/wiki/Leading_axis_theory">leading axis model</a> is to introduce a kind of index for multidimensional arrays that is easier to work with than list indices. The model introduces <a href="https://aplwiki.com/wiki/Cell">cells</a>, where a cell index is a list of any length up to the containing array's rank. General cell indices are discussed in the next section; first we introduce a special case, indices into major cells or Β―1-cells. These cells naturally form a list, so the index of a major cell is a single number. These indices can also be considered indices along the first axis, since an index along any axis is a single number.</p> <p>Ordering-based functions <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> only really make sense with major cell indices: while it's possible to order other indices as ravel indices, this probably isn't useful from a programming standpoint. Note that <code><span class='Function'>β</span></code> only uses the ordering in an incidental way, because it's defined to return the <em>first</em> index where a right argument cell is found. A mathematician would be more interested in a "pre-image" function that returns the set of all indices where a particular value appears. However, programming usefulness and consistency with the other search functions makes searching for the first index a reasonable choice.</p> -<p>Only one other functionβbut an important one!βdeals with cells rather than elements: <code><span class='Function'>β</span></code>, cell selection. Like dyadic <code><span class='Function'>ββββ½β</span></code> (depth 0) and <code><span class='Function'>/β</span></code> (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a vector of depth-1 elements. In each case the depth-1 arrays index into a single axis.</p> +<p>Only one other functionβbut an important one!βdeals with cells rather than elements: <code><span class='Function'>β</span></code>, cell selection. Like dyadic <code><span class='Function'>ββββ½β</span></code> (depth 0) and <code><span class='Function'>/β</span></code> (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis.</p> <h1 id="general-cell-indices">General cell indices</h1> <p>BQN does not use general cell indices directly, but it is useful to consider how they might work, and how a programmer might implement functions that use them in BQN if needed. The functions <code><span class='Function'>/</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> are the ones that can work with indices for multidimensional arrays but don't already. Here we will examine how multidimensional versions would work.</p> -<p>A cell index into an array of rank <code><span class='Value'>r</span></code> is a numeric vector of length <code><span class='Value'>l</span><span class='Function'>β€</span><span class='Value'>r</span></code>, which then refers to a cell of rank <code><span class='Value'>r</span><span class='Function'>-</span><span class='Value'>l</span></code>. In BQN, the cell at index <code><span class='Value'>i</span></code> of array <code><span class='Value'>a</span></code> is <code><span class='Value'>i</span><span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Value'>a</span></code>.</p> -<p>Because the shape of a cell index relates to the shape of the indexed array, it makes sense not to enclose cell indices, instead treating them as rows of an index array. A definition for <code><span class='Function'>β</span></code> for depth-1 left arguments of rank at least 1 follows: replace each row of the left argument with the indexed cell of the right, yielding a result with the same depth as the right argument and shape <code><span class='Value'>π¨</span><span class='Paren'>((</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Function'>βΎ</span><span class='Paren'>(</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>β’</span><span class='Value'>π©</span></code>.</p> -<p>To match this format, Range (<code><span class='Function'>β</span></code>) could be changed to return a flat array when given a shapeβwhat is now <code><span class='Function'>>β</span></code>. Following this pattern, Indices (<code><span class='Function'>/</span></code>) would also return a flat array, where the indices are rows: using the modified Range, <code><span class='Function'>β₯/β</span><span class='Composition'>β</span><span class='Function'>β’</span></code>. Here the result cannot retain the argument's array structure; it is always a rank-2 list of rows.</p> +<p>A cell index into an array of rank <code><span class='Value'>r</span></code> is a numeric list of length <code><span class='Value'>l</span><span class='Function'>β€</span><span class='Value'>r</span></code>, which then refers to a cell of rank <code><span class='Value'>r</span><span class='Function'>-</span><span class='Value'>l</span></code>. In BQN, the cell at index <code><span class='Value'>i</span></code> of array <code><span class='Value'>a</span></code> is <code><span class='Value'>i</span><span class='Function'><</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Value'>a</span></code>.</p> +<p>Because the shape of a cell index relates to the shape of the indexed array, it makes sense not to enclose cell indices, instead treating them as rows of an index array. A definition for <code><span class='Function'>β</span></code> for depth-1 left arguments of rank at least 1 follows: replace each row of the left argument with the indexed cell of the right, yielding a result with the same depth as the right argument and shape <code><span class='Value'>π¨</span><span class='Paren'>((</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Function'>βΎ</span><span class='Paren'>(</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>β’</span><span class='Value'>π©</span></code>.</p> +<p>To match this format, Range (<code><span class='Function'>β</span></code>) could be changed to return a flat array when given a shapeβwhat is now <code><span class='Function'>>β</span></code>. Following this pattern, Indices (<code><span class='Function'>/</span></code>) would also return a flat array, where the indices are rows: using the modified Range, <code><span class='Function'>β₯/β</span><span class='Modifier2'>β</span><span class='Function'>β’</span></code>. Here the result cannot retain the argument's array structure; it is always a rank-2 list of rows.</p> <p>The most interesting feature would be that <code><span class='Function'>β</span></code> could still allow a nested left argument. In this case each element of the left argument would be an array with row indices as before. However, each row can now index along multiple axes, allowing some adjacent axes to be dependent while others remain independent. This nicely unifies scatter-point and per-axis selection, and allows a mix of the two. However, it doesn't allow total freedom, as non-adjacent axes can't be combined except by also mixing in all axes in between.</p> <p>Group (<code><span class='Function'>β</span></code>) could accept the same index format for its index argument. Each depth-1 array in the left argument would correspond to multiple axes in the outer result array, but only a single axis in the argument and inner arrays. Because the ravel ordering of indices must be used to order cells of inner arrays, this modification is not quite as clean as the change to Select. It's also not so clearly useful, as the same results can be obtained by using numeric indices and reshaping the result.</p> <p>Overall it seems to me that the main use of cell indices of the type discussed here is for the Select primitive, and the other cases are somewhat contrived an awkward. So I've chosen not to support it in BQN at all.</p> diff --git a/docs/doc/join.html b/docs/doc/join.html index 744fa923..b65dc46e 100644 --- a/docs/doc/join.html +++ b/docs/doc/join.html @@ -1,6 +1,6 @@ <head><link href="../style.css" rel="stylesheet"/></head> <h1 id="join">Join</h1> -<p>Join (<code><span class='Function'>βΎ</span></code>) is an extension of the monadic function <a href="https://aplwiki.com/wiki/Raze">Raze</a> from A+ and J to arbitrary argument ranks. It has the same relationship to Join to, the dyadic function sharing the same glyph, as Unbox (<code><span class='Function'>></span></code>) does to Couple (<code><span class='Function'>β</span></code>): <code><span class='Value'>a</span><span class='Function'>β</span><span class='Value'>b</span></code> is <code><span class='Function'>></span><span class='Value'>a</span><span class='Ligature'>βΏ</span><span class='Value'>b</span></code> and <code><span class='Value'>a</span><span class='Function'>βΎ</span><span class='Value'>b</span></code> is <code><span class='Function'>βΎ</span><span class='Value'>a</span><span class='Ligature'>βΏ</span><span class='Value'>b</span></code>. While Unbox and Couple combine arrays (the elements of Unbox's argument, or the arguments themselves for Coups) along a new leading axis, Join and Join to combine them along the existing leading axis. Both Unbox and Join can also be called on a higher-rank array, causing Unbox to add multiple leading axes while Join combines elements along multiple existing axes.</p> +<p>Join (<code><span class='Function'>βΎ</span></code>) is an extension of the monadic function <a href="https://aplwiki.com/wiki/Raze">Raze</a> from A+ and J to arbitrary argument ranks. It has the same relationship to Join to, the dyadic function sharing the same glyph, as Merge (<code><span class='Function'>></span></code>) does to Couple (<code><span class='Function'>β</span></code>): <code><span class='Value'>a</span><span class='Function'>β</span><span class='Value'>b</span></code> is <code><span class='Function'>></span><span class='Value'>a</span><span class='Ligature'>βΏ</span><span class='Value'>b</span></code> and <code><span class='Value'>a</span><span class='Function'>βΎ</span><span class='Value'>b</span></code> is <code><span class='Function'>βΎ</span><span class='Value'>a</span><span class='Ligature'>βΏ</span><span class='Value'>b</span></code>. While Merge and Couple combine arrays (the elements of Merge's argument, or the arguments themselves for Couple) along a new leading axis, Join and Join to combine them along the existing leading axis. Both Merge and Join can also be called on a higher-rank array, causing Merge to add multiple leading axes while Join combines elements along multiple existing axes.</p> <p>Join can be used to combine several strings into a single string, like <code><span class='Value'>array.join</span><span class='Paren'>()</span></code> in Javascript (but it doesn't force the result to be a string).</p> <pre> <span class='Function'>βΎ</span><span class='String'>"time"</span><span class='Ligature'>βΏ</span><span class='String'>"to"</span><span class='Ligature'>βΏ</span><span class='String'>"join"</span><span class='Ligature'>βΏ</span><span class='String'>"some"</span><span class='Ligature'>βΏ</span><span class='String'>"words"</span> <span class='Value'>[</span> <span class='Value'>timetojoinsomewords</span> <span class='Value'>]</span> @@ -33,6 +33,6 @@ <span class='Number'>3</span> <span class='Number'>3</span> <span class='Number'>3</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Number'>5</span> <span class='Number'>5</span> <span class='Number'>5</span> <span class='Number'>5</span> <span class='Value'>β</span> </pre> -<p>Join has fairly strict requirements on the shapes of its argument elementsβalthough less strict than those of Unbox, which requires they all have identical shape. Suppose the argument to Join has rank <code><span class='Value'>m</span></code>. Each of its elements must have the same rank, <code><span class='Value'>n</span></code>, which is at least <code><span class='Value'>m</span></code>. The trailing shapes <code><span class='Value'>m</span><span class='Function'>β</span><span class='Composition'>β</span><span class='Function'>β’</span><span class='Modifier'>Β¨</span><span class='Value'>π©</span></code> must all be identical (the trailing shape <code><span class='Value'>m</span><span class='Function'>ββ’βΎ</span><span class='Value'>π©</span></code> of the result will match these shapes as well). The other entries in the leading shapes need not be the same, but the shape of an element along a particular axis must depend only on the location of the element along that axis in the full array. For a vector argument this imposes no restriction, since the one leading shape element is allowed to depend on position along the only axis. But for higher ranks the structure quickly becomes more rigid.</p> -<p>To state this requirement more formally in BQN, we say that there is some vector <code><span class='Value'>s</span></code> of length vectors, so that <code><span class='Paren'>(</span><span class='Function'>β’</span><span class='Modifier'>Β¨</span><span class='Value'>s</span><span class='Paren'>)</span><span class='Function'>β‘β’</span><span class='Value'>π©</span></code>. We require element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π©</span></code> to have shape <code><span class='Value'>i</span><span class='Function'>β</span><span class='Modifier'>Β¨</span><span class='Value'>s</span></code>. Then the first <code><span class='Value'>m</span></code> axes of the result are <code><span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Value'>s</span></code>.</p> +<p>Join has fairly strict requirements on the shapes of its argument elementsβalthough less strict than those of Merge, which requires they all have identical shape. Suppose the argument to Join has rank <code><span class='Value'>m</span></code>. Each of its elements must have the same rank, <code><span class='Value'>n</span></code>, which is at least <code><span class='Value'>m</span></code>. The trailing shapes <code><span class='Value'>m</span><span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'>β’</span><span class='Modifier'>Β¨</span><span class='Value'>π©</span></code> must all be identical (the trailing shape <code><span class='Value'>m</span><span class='Function'>ββ’βΎ</span><span class='Value'>π©</span></code> of the result will match these shapes as well). The other entries in the leading shapes need not be the same, but the shape of an element along a particular axis must depend only on the location of the element along that axis in the full array. For a list argument this imposes no restriction, since the one leading shape element is allowed to depend on position along the only axis. But for higher ranks the structure quickly becomes more rigid.</p> +<p>To state this requirement more formally in BQN, we say that there is some list <code><span class='Value'>s</span></code> of lists of lengths, so that <code><span class='Paren'>(</span><span class='Function'>β’</span><span class='Modifier'>Β¨</span><span class='Value'>s</span><span class='Paren'>)</span><span class='Function'>β‘β’</span><span class='Value'>π©</span></code>. We require element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>π©</span></code> to have shape <code><span class='Value'>i</span><span class='Function'>β</span><span class='Modifier'>Β¨</span><span class='Value'>s</span></code>. Then the first <code><span class='Value'>m</span></code> axes of the result are <code><span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Value'>s</span></code>.</p> diff --git a/docs/doc/logic.html b/docs/doc/logic.html index 87b5fad1..efd37938 100644 --- a/docs/doc/logic.html +++ b/docs/doc/logic.html @@ -1,15 +1,15 @@ <head><link href="../style.css" rel="stylesheet"/></head> <h1 id="logic-functions--and--or--not--also-span-">Logic functions: And, Or, Not (also Span)</h1> -<p>BQN retains the APL symbols <code><span class='Function'>β§</span></code> and <code><span class='Function'>β¨</span></code> for logical <em>and</em> and <em>or</em>, and changed APL's <code><span class='Value'>~</span></code> to <code><span class='Function'>Β¬</span></code> for <em>not</em>, since <code><span class='Value'>~</span></code> looks too much like <code><span class='Modifier'>Λ</span></code> and <code><span class='Function'>Β¬</span></code> is more common in mathematics today. Like J, BQN extends Not to the linear function <code><span class='Number'>1</span><span class='Composition'>βΈ</span><span class='Function'>-</span></code>. However, it discards <a href="https://aplwiki.com/wiki/GCD">GCD</a> and <a href="https://aplwiki.com/wiki/LCM">LCM</a> as extensions of And and Or, and instead uses bilinear extensions: And is identical to Times (<code><span class='Function'>Γ</span></code>), while Or is <code><span class='Function'>Γ</span><span class='Composition'>βΎ</span><span class='Function'>Β¬</span></code>, following De Morgan's laws (other ways of obtaining a function for Or give an equivalent resultβthere is only one bilinear extension).</p> +<p>BQN retains the APL symbols <code><span class='Function'>β§</span></code> and <code><span class='Function'>β¨</span></code> for logical <em>and</em> and <em>or</em>, and changed APL's <code><span class='Value'>~</span></code> to <code><span class='Function'>Β¬</span></code> for <em>not</em>, since <code><span class='Value'>~</span></code> looks too much like <code><span class='Modifier'>Λ</span></code> and <code><span class='Function'>Β¬</span></code> is more common in mathematics today. Like J, BQN extends Not to the linear function <code><span class='Number'>1</span><span class='Modifier2'>βΈ</span><span class='Function'>-</span></code>. However, it discards <a href="https://aplwiki.com/wiki/GCD">GCD</a> and <a href="https://aplwiki.com/wiki/LCM">LCM</a> as extensions of And and Or, and instead uses bilinear extensions: And is identical to Times (<code><span class='Function'>Γ</span></code>), while Or is <code><span class='Function'>Γ</span><span class='Modifier2'>βΎ</span><span class='Function'>Β¬</span></code>, following De Morgan's laws (other ways of obtaining a function for Or give an equivalent resultβthere is only one bilinear extension).</p> <p>If the arguments are probabilities of independent events, then an extended function gives the probability of the boolean function on their outcomes (for example, if <em>A</em> occurs with probability <code><span class='Value'>a</span></code> and <em>B</em> with probability <code><span class='Value'>b</span></code> independent of <em>A</em>, then <em>A</em> or <em>B</em> occurs with probability <code><span class='Value'>a</span><span class='Function'>β¨</span><span class='Value'>b</span></code>). These extensions have also been used in complexity theory, because they allow mathematicians to transfer a logical circuit from the discrete to the continuous domain in order to use calculus on it.</p> <p>Both valences of <code><span class='Function'>Β¬</span></code> are equivalent to the fork <code><span class='Number'>1</span><span class='Function'>+-</span></code>. The dyadic valence, called "Span", computes the number of integers in the range from <code><span class='Value'>π©</span></code> to <code><span class='Value'>π¨</span></code>, inclusive, when both arguments are integers and <code><span class='Value'>π©</span><span class='Function'>β€</span><span class='Value'>π¨</span></code> (note the reversed order, which is used for consistency with subtraction). This function has many uses, and in particular is relevant to the <a href="windows.html">Windows</a> function.</p> <h2 id="definitions">Definitions</h2> <p>We define:</p> <pre><span class='Function'>Not</span> <span class='Gets'>β</span> <span class='Number'>1</span><span class='Function'>+-</span> <span class='Comment'># also Span </span><span class='Function'>And</span> <span class='Gets'>β</span> <span class='Function'>Γ</span> -<span class='Function'>Or</span> <span class='Gets'>β</span> <span class='Function'>Γ</span><span class='Composition'>βΎ</span><span class='Function'>Β¬</span> +<span class='Function'>Or</span> <span class='Gets'>β</span> <span class='Function'>Γ</span><span class='Modifier2'>βΎ</span><span class='Function'>Β¬</span> </pre> -<p>Note that <code><span class='Function'>Β¬</span><span class='Modifier'>βΌ</span> <span class='Gets'>ββ</span> <span class='Function'>Β¬</span></code>, since the first added 1 will be negated but the second won't; the two 1s cancel leaving two subtractions, and <code><span class='Function'>-</span><span class='Modifier'>βΌ</span> <span class='Gets'>ββ</span> <span class='Function'>-</span></code>. An alternate definition of Or that matches the typical formula from probability theory is</p> +<p>Note that <code><span class='Function'>Β¬</span><span class='Modifier'>βΌ</span> <span class='Gets'>ββ</span> <span class='Function'>Β¬</span></code>, since when applying <code><span class='Function'>Β¬</span></code> twice the first added 1 will be negated but the second won't; the two 1s cancel leaving two subtractions, and <code><span class='Function'>-</span><span class='Modifier'>βΌ</span> <span class='Gets'>ββ</span> <span class='Function'>-</span></code>. An alternate definition of Or that matches the typical formula from probability theory is</p> <pre><span class='Function'>Or</span> <span class='Gets'>β</span> <span class='Function'>+-Γ</span> </pre> <h2 id="examples">Examples</h2> @@ -37,6 +37,6 @@ <p>A secondary reason is that the GCD falls short as an extension of Or, because its identity element 0 is not total. <code><span class='Number'>0</span><span class='Function'>β¨</span><span class='Value'>x</span></code>, for a real number <code><span class='Value'>x</span></code>, is actually equal to <code><span class='Function'>|</span><span class='Value'>x</span></code> and not <code><span class='Value'>x</span></code>: for example, <code><span class='Number'>0</span><span class='Function'>β¨</span><span class='Number'>Β―2</span></code> is <code><span class='Number'>2</span></code> in APL. This means the identity <code><span class='Number'>0</span><span class='Function'>β¨</span><span class='Value'>x</span> <span class='Gets'>ββ</span> <span class='Value'>x</span></code> isn't reliable in APL.</p> <h2 id="identity-elements">Identity elements</h2> <p>It's common to apply <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span></code> or <code><span class='Function'>β¨</span><span class='Modifier'>Β΄</span></code> to a list (checking whether all elements are true and whether any are true, respectively), and so it's important for extensions to And and Or to share their identity element. Minimum and Maximum do match And and Or when restricted to booleans, but they have different identity elements. It would be dangerous to use Maximum to check whether any element of a list is true because <code><span class='Function'>>β</span><span class='Modifier'>Β΄</span><span class='Bracket'>β¨β©</span></code> yields <code><span class='Number'>Β―β</span></code> instead of <code><span class='Number'>0</span></code>βa bug waiting to happen. Always using <code><span class='Number'>0</span></code> as a left argument to <code><span class='Function'>β</span><span class='Modifier'>Β΄</span></code> fixes this problem but requires more work from the programmer, making errors more likely.</p> -<p>It is easy to prove that the bilinear extensions have the identity elements we want. Of course <code><span class='Number'>1</span><span class='Function'>β§</span><span class='Value'>x</span></code> is <code><span class='Number'>1</span><span class='Function'>Γ</span><span class='Value'>x</span></code>, or <code><span class='Value'>x</span></code>, and <code><span class='Number'>0</span><span class='Function'>β¨</span><span class='Value'>x</span></code> is <code><span class='Number'>0</span><span class='Function'>Γ</span><span class='Composition'>βΎ</span><span class='Function'>Β¬</span><span class='Value'>x</span></code>, or <code><span class='Function'>Β¬</span><span class='Number'>1</span><span class='Function'>ΓΒ¬</span><span class='Value'>x</span></code>, giving <code><span class='Function'>¬¬</span><span class='Value'>x</span></code> or <code><span class='Value'>x</span></code> again. Both functions are commutative, so these identities are double-sided.</p> +<p>It is easy to prove that the bilinear extensions have the identity elements we want. Of course <code><span class='Number'>1</span><span class='Function'>β§</span><span class='Value'>x</span></code> is <code><span class='Number'>1</span><span class='Function'>Γ</span><span class='Value'>x</span></code>, or <code><span class='Value'>x</span></code>, and <code><span class='Number'>0</span><span class='Function'>β¨</span><span class='Value'>x</span></code> is <code><span class='Number'>0</span><span class='Function'>Γ</span><span class='Modifier2'>βΎ</span><span class='Function'>Β¬</span><span class='Value'>x</span></code>, or <code><span class='Function'>Β¬</span><span class='Number'>1</span><span class='Function'>ΓΒ¬</span><span class='Value'>x</span></code>, giving <code><span class='Function'>¬¬</span><span class='Value'>x</span></code> or <code><span class='Value'>x</span></code> again. Both functions are commutative, so these identities are double-sided.</p> <p>Other logical identities do not necessarily hold. For example, in boolean logic And distributes over Or and vice-versa: <code><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>b</span><span class='Function'>β¨</span><span class='Value'>c</span> <span class='Gets'>ββ</span> <span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>β¨</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>c</span><span class='Paren'>)</span></code>. But substituting <code><span class='Function'>Γ</span></code> for <code><span class='Function'>β§</span></code> and <code><span class='Function'>+-Γ</span></code> for <code><span class='Function'>β¨</span></code> we find that the left hand side is <code><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>c</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>b</span><span class='Function'>Γ</span><span class='Value'>c</span><span class='Paren'>)</span></code> while the right gives <code><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>b</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>c</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>b</span><span class='Function'>Γ</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>c</span><span class='Paren'>)</span></code>. These are equivalent for arbitrary <code><span class='Value'>b</span></code> and <code><span class='Value'>c</span></code> only if <code><span class='Value'>a</span><span class='Function'>=</span><span class='Value'>a</span><span class='Function'>Γ</span><span class='Value'>a</span></code>, that is, <code><span class='Value'>a</span></code> is 0 or 1. In terms of probabilities the difference when <code><span class='Value'>a</span></code> is not boolean is caused by failure of independence. On the left hand side, the two arguments of every logical function are independent. On the right hand side, each pair of arguments to <code><span class='Function'>β§</span></code> are independent, but the two arguments to <code><span class='Function'>β¨</span></code>, <code><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>b</span></code> and <code><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>c</span></code>, are not. The relationship between these arguments means that logical equivalences no longer apply.</p> diff --git a/docs/doc/transpose.html b/docs/doc/transpose.html index d2629546..43b51b48 100644 --- a/docs/doc/transpose.html +++ b/docs/doc/transpose.html @@ -2,7 +2,7 @@ <h1 id="transpose">Transpose</h1> <p>As in APL, Transpose (<code><span class='Function'>β</span></code>) is a tool for rearranging the axes of an array. BQN's version is tweaked to align better with the leading axis model and make common operations easier.</p> <h2 id="monadic-transpose">Monadic Transpose</h2> -<p>Transposing a matrix exchanges its axes, mirroring it across the diagonal. APL extends the function to any rank by reversing all axes, but this generalization isn't very natural and is almost never used. The main reason for it is to maintain the equivalence <code><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>ββ</span> <span class='Value'>a</span> <span class='Function'>MP</span><span class='Composition'>βΎ</span><span class='Function'>β</span> <span class='Value'>b</span></code>, where <code><span class='Function'>MP</span> <span class='Gets'>β</span> <span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>Γ</span><span class='Composition'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span></code> is the generalized matrix product. But even here APL's Transpose is suspect. It does much more work than it needs to, as we'll see.</p> +<p>Transposing a matrix exchanges its axes, mirroring it across the diagonal. APL extends the function to any rank by reversing all axes, but this generalization isn't very natural and is almost never used. The main reason for it is to maintain the equivalence <code><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>ββ</span> <span class='Value'>a</span> <span class='Function'>MP</span><span class='Modifier2'>βΎ</span><span class='Function'>β</span> <span class='Value'>b</span></code>, where <code><span class='Function'>MP</span> <span class='Gets'>β</span> <span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>Γ</span><span class='Modifier2'>β</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>β</span></code> is the generalized matrix product. But even here APL's Transpose is suspect. It does much more work than it needs to, as we'll see.</p> <p>BQN's transpose takes the first axis of its argument and moves it to the end.</p> <pre> <span class='Function'>β’</span> <span class='Value'>a23456</span> <span class='Gets'>β</span> <span class='Function'>β</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span><span class='Ligature'>βΏ</span><span class='Number'>5</span><span class='Ligature'>βΏ</span><span class='Number'>6</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Number'>6</span> <span class='Value'>]</span> @@ -11,7 +11,7 @@ </pre> <p>On the argument's ravel, this looks like a simple 2-dimensional transpose: one axis is exchanged with a compound axis made up of the other axes. Here we transpose a rank 3 matrix:</p> <pre> <span class='Value'>a322</span> <span class='Gets'>β</span> <span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Function'>β₯β</span><span class='Number'>12</span> - <span class='Function'>β</span><span class='Composition'>β</span><span class='Function'><</span><span class='Composition'>β</span><span class='Function'>β</span> <span class='Value'>a322</span> + <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'><</span><span class='Modifier2'>β</span><span class='Function'>β</span> <span class='Value'>a322</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>0</span> <span class='Number'>4</span> <span class='Number'>8</span> @@ -26,7 +26,7 @@ <span class='Value'>β</span> </pre> <p>But, reading in ravel order, the argument and result have exactly the same element ordering as for the rank 2 matrix <code><span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Value'>a322</span></code>:</p> -<pre> <span class='Function'>β</span><span class='Composition'>β</span><span class='Function'><</span><span class='Composition'>β</span><span class='Function'>β</span> <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Value'>a322</span> +<pre> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'><</span><span class='Modifier2'>β</span><span class='Function'>β</span> <span class='Function'>β₯</span><span class='Modifier'>Λ</span> <span class='Value'>a322</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Number'>0</span> <span class='Number'>1</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>0</span> <span class='Number'>4</span> <span class='Number'>8</span> @@ -36,27 +36,27 @@ <span class='Value'>β</span> <span class='Value'>β</span> </pre> -<p>To exchange multiple axes, use the Power operator. Like Rotate, a negative power will move axes in the other direction. In particular, to move the last axis to the front, use Inverse (as you might expect, this exactly inverts <code><span class='Function'>β</span></code>).</p> -<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Number'>3</span> <span class='Value'>a23456</span> +<p>To exchange multiple axes, use the Power modifier. Like Rotate, a negative power will move axes in the other direction. In particular, to move the last axis to the front, use Inverse (as you might expect, this exactly inverts <code><span class='Function'>β</span></code>).</p> +<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Number'>3</span> <span class='Value'>a23456</span> <span class='Value'>[</span> <span class='Number'>5</span> <span class='Number'>6</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Value'>]</span> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span> <span class='Value'>a23456</span> <span class='Value'>[</span> <span class='Number'>6</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Value'>]</span> </pre> -<p>In fact, we have <code><span class='Function'>β’β</span><span class='Composition'>β</span><span class='Value'>k</span> <span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Value'>k</span><span class='Function'>β½β’</span><span class='Value'>a</span></code> for any number <code><span class='Value'>k</span></code> and array <code><span class='Value'>a</span></code>.</p> -<p>To move axes other than the first, use the Rank operator in order to leave initial axes untouched. A rank of <code><span class='Value'>k</span><span class='Function'>></span><span class='Number'>0</span></code> transposes only the last <code><span class='Value'>k</span></code> axes while a rank of <code><span class='Value'>k</span><span class='Function'><</span><span class='Number'>0</span></code> ignores the first <code><span class='Function'>|</span><span class='Value'>k</span></code> axes.</p> -<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Number'>3</span> <span class='Value'>a23456</span> +<p>In fact, we have <code><span class='Function'>β’β</span><span class='Modifier2'>β</span><span class='Value'>k</span> <span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Value'>k</span><span class='Function'>β½β’</span><span class='Value'>a</span></code> for any number <code><span class='Value'>k</span></code> and array <code><span class='Value'>a</span></code>.</p> +<p>To move axes other than the first, use the Rank modifier in order to leave initial axes untouched. A rank of <code><span class='Value'>k</span><span class='Function'>></span><span class='Number'>0</span></code> transposes only the last <code><span class='Value'>k</span></code> axes while a rank of <code><span class='Value'>k</span><span class='Function'><</span><span class='Number'>0</span></code> ignores the first <code><span class='Function'>|</span><span class='Value'>k</span></code> axes.</p> +<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Number'>3</span> <span class='Value'>a23456</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Number'>5</span> <span class='Number'>6</span> <span class='Number'>4</span> <span class='Value'>]</span> </pre> <p>And of course, Rank and Power can be combined to do more complicated transpositions: move a set of contiguous axes with any starting point and length to the end.</p> -<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Composition'>β</span><span class='Number'>Β―1</span> <span class='Value'>a23456</span> +<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Modifier2'>β</span><span class='Number'>Β―1</span> <span class='Value'>a23456</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>6</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>5</span> <span class='Value'>]</span> </pre> <p>Using these forms, we can state BQN's generalized matrix product swapping rule:</p> -<pre><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>ββ</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>β β’</span><span class='Value'>a</span><span class='Paren'>)</span> <span class='Value'>a</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Composition'>βΈ</span><span class='Function'>MP</span><span class='Composition'>β</span><span class='Function'>β</span> <span class='Value'>b</span> +<pre><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>ββ</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>β β’</span><span class='Value'>a</span><span class='Paren'>)</span> <span class='Value'>a</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Modifier2'>βΈ</span><span class='Function'>MP</span><span class='Modifier2'>β</span><span class='Function'>β</span> <span class='Value'>b</span> </pre> <p>Certainly not as concise as APL's version, but not a horror either. BQN's rule is actually more parsimonious in that it only performs the axis exchanges necessary for the computation: it moves the two axes that will be paired with the matrix product into place before the product, and directly exchanges all axes afterwards. Each of these steps is equivalent in terms of data movement to a matrix transpose, the simplest nontrivial transpose to perform. Also remember that for two-dimensional matrices both kinds of transposition are the same, and APL's rule holds in BQN.</p> <p>Axis permutations of the types we've shown generate the complete permutation group on any number of axes, so you could produce any transposition you want with the right sequence of monadic transpositions with Rank. However, this can be unintuitive and tedious. What if you want to transpose the first three axes, leaving the rest alone? With monadic Transpose you have to send some axes to the end, then bring them back to the beginning. For example [following four or five failed tries]:</p> -<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Composition'>β</span><span class='Number'>Β―2</span> <span class='Function'>β</span> <span class='Value'>a23456</span> <span class='Comment'># Restrict Transpose to the first three axes +<pre> <span class='Function'>β’</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Modifier2'>β</span><span class='Number'>Β―2</span> <span class='Function'>β</span> <span class='Value'>a23456</span> <span class='Comment'># Restrict Transpose to the first three axes </span><span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>2</span> <span class='Number'>5</span> <span class='Number'>6</span> <span class='Value'>]</span> </pre> <p>In a case like this BQN's Dyadic transpose is much easier.</p> @@ -67,7 +67,7 @@ <span class='Function'>β’</span> <span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>0</span> <span class='Function'>β</span> <span class='Value'>a23456</span> <span class='Comment'># Don't worry too much about this case though </span><span class='Value'>[</span> <span class='Number'>5</span> <span class='Number'>2</span> <span class='Number'>3</span> <span class='Value'>]</span> </pre> -<p>Since this kind of rearrangement can be counterintuitive, it's often easier to use <code><span class='Function'>β</span><span class='Modifier'>βΌ</span></code> when specifying all axes. If <code><span class='Value'>p</span><span class='Function'>β‘</span><span class='Composition'>β</span><span class='Function'>β β’</span><span class='Value'>a</span></code>, then we have <code><span class='Function'>β’</span><span class='Value'>p</span><span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Value'>p</span><span class='Function'>ββ’</span><span class='Value'>a</span></code>.</p> +<p>Since this kind of rearrangement can be counterintuitive, it's often easier to use <code><span class='Function'>β</span><span class='Modifier'>βΌ</span></code> when specifying all axes. If <code><span class='Value'>p</span><span class='Function'>β‘</span><span class='Modifier2'>β</span><span class='Function'>β β’</span><span class='Value'>a</span></code>, then we have <code><span class='Function'>β’</span><span class='Value'>p</span><span class='Function'>β</span><span class='Modifier'>βΌ</span><span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Value'>p</span><span class='Function'>ββ’</span><span class='Value'>a</span></code>.</p> <pre> <span class='Function'>β’</span> <span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>4</span> <span class='Function'>β</span><span class='Modifier'>βΌ</span> <span class='Value'>a23456</span> <span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>5</span> <span class='Number'>4</span> <span class='Number'>2</span> <span class='Number'>6</span> <span class='Value'>]</span> </pre> @@ -79,10 +79,10 @@ <pre> <span class='Function'>β’</span> <span class='Number'>2</span> <span class='Function'>β</span> <span class='Value'>a23456</span> <span class='Comment'># Restrict Transpose to the first three axes </span><span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>4</span> <span class='Number'>2</span> <span class='Number'>5</span> <span class='Number'>6</span> <span class='Value'>]</span> </pre> -<p>Finally, it's worth noting that, as monadic Transpose moves the first axis to the end, it's equivalent to dyadic Transpose with a "default" left argument: <code><span class='Paren'>(</span><span class='Function'>β </span><span class='Composition'>β</span><span class='Function'>β’-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code>.</p> +<p>Finally, it's worth noting that, as monadic Transpose moves the first axis to the end, it's equivalent to dyadic Transpose with a "default" left argument: <code><span class='Paren'>(</span><span class='Function'>β </span><span class='Modifier2'>β</span><span class='Function'>β’-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>.</p> <h2 id="definitions">Definitions</h2> <p>Here we define the two valences of Transpose more precisely.</p> -<p>A non-array right argument to Transpose is always boxed to get a scalar array before doing anything else.</p> -<p>Monadic transpose is identical to <code><span class='Paren'>(</span><span class='Function'>β </span><span class='Composition'>β</span><span class='Function'>β’-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code>, except that for scalar arguments it returns the array unchanged rather than giving an error.</p> -<p>In Dyadic transpose, the left argument is a number or numeric array of rank 1 or less, and <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Composition'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. Define the result rank <code><span class='Value'>r</span><span class='Gets'>β</span><span class='Paren'>(</span><span class='Function'>β β’</span><span class='Value'>π©</span><span class='Paren'>)</span><span class='Function'>-+</span><span class='Modifier'>Β΄</span><span class='Function'>Β¬β</span><span class='Value'>π¨</span></code> to be the argument rank minus the number of duplicate entries in the left argument. We require <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span><span class='Value'>π¨</span><span class='Function'><</span><span class='Value'>r</span></code>. Bring <code><span class='Value'>π¨</span></code> to full length by appending the missing indices: <code><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Gets'>β©</span><span class='Value'>π¨</span><span class='Paren'>(</span><span class='Function'>Β¬</span><span class='Composition'>β</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>/β’</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>r</span></code>. Now the result shape is defined to be <code><span class='Function'>β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span><span class='Function'>ββ’</span><span class='Value'>π©</span></code>. Element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> of the result <code><span class='Value'>z</span></code> is element <code><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>i</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>π©</span></code> of the argument.</p> +<p>A non-array right argument to Transpose is always enclosed to get a scalar array before doing anything else.</p> +<p>Monadic transpose is identical to <code><span class='Paren'>(</span><span class='Function'>β </span><span class='Modifier2'>β</span><span class='Function'>β’-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>, except that for scalar arguments it returns the array unchanged rather than giving an error.</p> +<p>In Dyadic transpose, the left argument is a number or numeric array of rank 1 or less, and <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. Define the result rank <code><span class='Value'>r</span><span class='Gets'>β</span><span class='Paren'>(</span><span class='Function'>β β’</span><span class='Value'>π©</span><span class='Paren'>)</span><span class='Function'>-+</span><span class='Modifier'>Β΄</span><span class='Function'>Β¬β</span><span class='Value'>π¨</span></code> to be the argument rank minus the number of duplicate entries in the left argument. We require <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span><span class='Value'>π¨</span><span class='Function'><</span><span class='Value'>r</span></code>. Bring <code><span class='Value'>π¨</span></code> to full length by appending the missing indices: <code><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Gets'>β©</span><span class='Value'>π¨</span><span class='Paren'>(</span><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>/β’</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>r</span></code>. Now the result shape is defined to be <code><span class='Function'>β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span><span class='Function'>ββ’</span><span class='Value'>π©</span></code>. Element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> of the result <code><span class='Value'>z</span></code> is element <code><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>i</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>π©</span></code> of the argument.</p> diff --git a/docs/doc/windows.html b/docs/doc/windows.html index 117acc08..27b9be00 100644 --- a/docs/doc/windows.html +++ b/docs/doc/windows.html @@ -1,6 +1,6 @@ <head><link href="../style.css" rel="stylesheet"/></head> <h1 id="windows">Windows</h1> -<p>In BQN, it's strongly preferred to use functions, and not operators (modifiers and compositions), for array manipulation. Functions are simpler as they have fewer moving parts. They are more concrete, since the array results can always be viewed right away. They are easier to implement with reasonable performance as well, since there is no need to recognize many possible function operands as special cases.</p> +<p>In BQN, it's strongly preferred to use functions, and not modifiers, for array manipulation. Functions are simpler as they have fewer moving parts. They are more concrete, since the array results can always be viewed right away. They are easier to implement with reasonable performance as well, since there is no need to recognize many possible function operands as special cases.</p> <p>The Window function replaces APL's Windowed Reduction, J's more general Infix operator, and Dyalog's Stencil, which is adapted from one case of J's Cut operator.</p> <h2 id="definition">Definition</h2> <p>We'll start with the one-axis case. Here Window's left argument is a number between <code><span class='Number'>0</span></code> and <code><span class='Number'>1</span><span class='Function'>+β </span><span class='Value'>π©</span></code>. The result is composed of slices of <code><span class='Value'>π©</span></code> (contiguous sections of major cells) with length <code><span class='Value'>π¨</span></code>, starting at each possible index in order.</p> @@ -18,10 +18,10 @@ <span class='Number'>5</span><span class='Function'>β</span><span class='Number'>2</span><span class='Function'>β</span><span class='String'>"abcdefg"</span> <span class='Value'>[</span> <span class='Value'>cdefg</span> <span class='Value'>]</span> </pre> -<p>Windows differs from Prefixes and Suffixes in that it doesn't add a layer of nesting (it doesn't box each slice). This is possible because the slices have a fixed size.</p> +<p>Windows differs from Prefixes and Suffixes in that it doesn't add a layer of nesting (it doesn't enclose each slice). This is possible because the slices have a fixed size.</p> <h3 id="multiple-dimensions">Multiple dimensions</h3> -<p>The above description applies to a higher-rank right argument. As an example, we'll look at two-row slices of a shape <code><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span></code> array. For convenience, we will box each slice. Note that slices always have the same rank as the argument array.</p> -<pre> <span class='Function'><</span><span class='Composition'>β</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Function'>β</span><span class='String'>"0123"</span><span class='Function'>βΎ</span><span class='String'>"abcd"</span><span class='Function'>β</span><span class='String'>"ABCD"</span> +<p>The above description applies to a higher-rank right argument. As an example, we'll look at two-row slices of a shape <code><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span></code> array. For convenience, we will enclose each slice. Note that slices always have the same rank as the argument array.</p> +<pre> <span class='Function'><</span><span class='Modifier2'>β</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Function'>β</span><span class='String'>"0123"</span><span class='Function'>βΎ</span><span class='String'>"abcd"</span><span class='Function'>β</span><span class='String'>"ABCD"</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Number'>0123</span> <span class='Value'>abcd</span> @@ -30,7 +30,7 @@ <span class='Value'>β</span> </pre> <p>Passing a list as the left argument to Windows takes slices along any number of leading axes. Here are all the shape <code><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span></code> slices:</p> -<pre> <span class='Function'><</span><span class='Composition'>β</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Function'>β</span><span class='String'>"0123"</span><span class='Function'>βΎ</span><span class='String'>"abcd"</span><span class='Function'>β</span><span class='String'>"ABCD"</span> +<pre> <span class='Function'><</span><span class='Modifier2'>β</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Function'>β</span><span class='String'>"0123"</span><span class='Function'>βΎ</span><span class='String'>"abcd"</span><span class='Function'>β</span><span class='String'>"ABCD"</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Value'>β</span> <span class='Number'>01</span> <span class='Number'>12</span> <span class='Number'>23</span> @@ -45,8 +45,8 @@ <p>The slices are naturally arranged along multiple dimensions according to their starting index. Once again the equivalence <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>l</span><span class='Function'>β</span><span class='Value'>x</span></code> ββ <code><span class='Value'>l</span><span class='Function'>β</span><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>x</span></code> holds, provided <code><span class='Value'>i</span></code> and <code><span class='Value'>l</span></code> have the same length.</p> <p>If the left argument has length <code><span class='Number'>0</span></code>, then the argument is not sliced along any dimensions. The only slice that resultsβthe entire argumentβis then arranged along an additional zero dimensions. In the end, the result is the same as the argument.</p> <h3 id="more-formally">More formally</h3> -<p><code><span class='Value'>π©</span></code> is an array. <code><span class='Value'>π¨</span></code> is a number or numeric list or scalar with <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Composition'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. The result <code><span class='Value'>z</span></code> has shape <code><span class='Value'>π¨</span><span class='Function'>βΎΒ¬</span><span class='Composition'>β</span><span class='Value'>π¨</span><span class='Composition'>βΎ</span><span class='Paren'>((</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Function'>β’</span><span class='Value'>π©</span></code>, and element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> is <code><span class='Value'>π©</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Paren'>(</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)(</span><span class='Function'>β+</span><span class='Composition'>βΎ</span><span class='Paren'>((</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Value'>i</span></code>.</p> -<p>Using <a href="group.html">Group</a> we could also write <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> ββ <code><span class='Value'>π©</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Function'>β</span><span class='Composition'>β</span><span class='Function'>β </span><span class='Paren'>)</span><span class='Function'>β’</span><span class='Value'>π©</span><span class='Paren'>)</span> <span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Composition'>β</span><span class='Function'>β</span> <span class='Value'>i</span></code>.</p> +<p><code><span class='Value'>π©</span></code> is an array. <code><span class='Value'>π¨</span></code> is a number or numeric list or scalar with <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. The result <code><span class='Value'>z</span></code> has shape <code><span class='Value'>π¨</span><span class='Function'>βΎΒ¬</span><span class='Modifier2'>β</span><span class='Value'>π¨</span><span class='Modifier2'>βΎ</span><span class='Paren'>((</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Function'>β’</span><span class='Value'>π©</span></code>, and element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> is <code><span class='Value'>π©</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Paren'>(</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)(</span><span class='Function'>β+</span><span class='Modifier2'>βΎ</span><span class='Paren'>((</span><span class='Function'>β </span><span class='Value'>π¨</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Value'>i</span></code>.</p> +<p>Using <a href="group.html">Group</a> we could also write <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> ββ <code><span class='Value'>π©</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'>β </span><span class='Paren'>)</span><span class='Function'>β’</span><span class='Value'>π©</span><span class='Paren'>)</span> <span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Modifier2'>β</span><span class='Function'>β</span> <span class='Value'>i</span></code>.</p> <h2 id="symmetry">Symmetry</h2> <p>Let's look at an earlier example, along with its transpose.</p> <pre> <span class='Brace'>{</span><span class='Bracket'>β¨</span><span class='Value'>π©</span><span class='Separator'>,</span><span class='Function'>β</span><span class='Value'>π©</span><span class='Bracket'>β©</span><span class='Brace'>}</span><span class='Number'>5</span><span class='Function'>β</span><span class='String'>"abcdefg"</span> @@ -61,7 +61,7 @@ <span class='Value'>β</span> </pre> <p>Although the two arrays have different shapes, they are identical where they overlap.</p> -<pre> <span class='Function'>β‘</span><span class='Composition'>β</span><span class='Paren'>(</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Composition'>β</span><span class='Function'>β</span><span class='Number'>5</span><span class='Function'>β</span><span class='String'>"abcdefg"</span> +<pre> <span class='Function'>β‘</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>β</span><span class='Number'>5</span><span class='Function'>β</span><span class='String'>"abcdefg"</span> <span class='Number'>1</span> </pre> <p>In other words, the i'th element of slice j is the same as the j'th element of slice i: it is the <code><span class='Value'>i</span><span class='Function'>+</span><span class='Value'>j</span></code>'th element of the argument. So transposing still gives a possible result of Windows, but with a different slice length.</p> @@ -84,7 +84,7 @@ <span class='Value'>[</span> <span class='Number'>3</span> <span class='Number'>2</span> <span class='Number'>1</span> <span class='Number'>1</span> <span class='Value'>]</span> </pre> <p>This method extends to any number of initial elements. We can modify the running sum above to keep the length constant by starting with two zeros.</p> -<pre> <span class='Paren'>((</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Paren'>(</span><span class='Number'>2</span><span class='Function'>β₯</span><span class='Number'>0</span><span class='Paren'>)</span><span class='Composition'>βΈ</span><span class='Function'>βΎ</span><span class='Paren'>)</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Number'>3</span><span class='Bracket'>β©</span> +<pre> <span class='Paren'>((</span><span class='Function'>+</span><span class='Modifier'>Β΄</span><span class='Function'><</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Function'>β β</span><span class='Paren'>(</span><span class='Number'>2</span><span class='Function'>β₯</span><span class='Number'>0</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>βΎ</span><span class='Paren'>)</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Number'>3</span><span class='Bracket'>β©</span> <span class='Value'>[</span> <span class='Number'>2</span> <span class='Number'>8</span> <span class='Number'>8</span> <span class='Number'>7</span> <span class='Number'>5</span> <span class='Number'>8</span> <span class='Value'>]</span> </pre> diff --git a/docs/problems.html b/docs/problems.html index 56467ba7..5f053696 100644 --- a/docs/problems.html +++ b/docs/problems.html @@ -5,7 +5,7 @@ <h3 id="empty-arrays-lose-type-information">Empty arrays lose type information</h3> <p>A pretty fundamental problem with dynamically-typed array languages. Prototypes are intended to solve it, but they don't really. It doesn't help that the notion of type is fluid: elements of an array in one moment can be axis lengths in the next; did the numeric value go from not being type information to being type information? Inferred type might help here, particularly the ability of one part of the program to ask another part for type information during compilation. But that needs to be specified if programmers are going to rely on it, which sounds difficult.</p> <h3 id="control-flow-with-function-selection-has-awkward-syntax">Control flow with function selection has awkward syntax</h3> -<p>At the moment BQN has no control structures, instead preferring function recursion, iteration, and selection. Selection is awkward because the result of selecting from a list of functions must be a value syntactically. It also often doesn't need an argument, since it can refer to values from the containing function because of lexical scoping. There should probably be a function that takes a function argument and invokes it, possible with an empty list as a dummy left argument. Somewhat like the operator <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code>. But also: should a <code><span class='Brace'>{}</span></code> object that doesn't refer to its arguments be special? Some kind of "block" construct?</p> +<p>At the moment BQN has no control structures, instead preferring function recursion, iteration, and selection. Selection is awkward because the result of selecting from a list of functions is a subject syntactically. It also often doesn't need an argument, since it can refer to values from the containing function because of lexical scoping. There should possibly be a function that takes a function argument and invokes it, possible with an empty list as a dummy left argument. Somewhat like the modifier <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code>.</p> <p><em>Potentially solved by multiple headers, blocks, and block returns. Needs reevaluation later.</em></p> <h3 id="incoherent-monad-dyad-builtin-pairs">Incoherent monad-dyad builtin pairs</h3> <p>BQN inherits the functions <code><span class='Function'>+Γββ|</span></code>, and adds the functions <code><span class='Function'>β§β¨<>β β‘β’ββ·</span></code>, that are only paired for their glyphs and not for any other reason (that is, both function valences match the symbol but they don't match with each other). I find there are just not enough good glyphs to separate all of these out, but I'm sure the pairings could be improved.</p> @@ -14,7 +14,7 @@ <h3 id="tacit-and-one-line-functions-are-hard-to-debug">Tacit and one-line functions are hard to debug</h3> <p>This problem hasn't manifested yet as BQN has no debugger, but it's something to keep in mind. Traditional line-by-line debuggers don't work when the line is doing so much work. Something like J's dissect or some kind of hybrid would probably do better.</p> <h3 id="search-function-depth">Search function depth</h3> -<p>The simplest way to define a search function like Index Of is to require the left argument to be a list, and search for an element that matches the right argument. But this means you can only search for one element at a time, which is annoying and doesn't work for Progressive Index Of. So we instead treat the searched argument as a list of major cells. Then we decide to search for cells of the other argument that have the same rank as those cells, since only cells with the same rank can match. That's a little strange for Bins, where it still makes sense to compare cells of different ranks. Furthermore, the result of any search function is always an array. To search for a single element and get an unboxed number, you need something like <code><span class='Value'>list</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΎ</span><span class='Function'><</span><span class='Value'>elt</span></code>.</p> +<p>The simplest way to define a search function like Index Of is to require the left argument to be a list, and search for an element that matches the right argument. But this means you can only search for one element at a time, which is annoying and doesn't work for Progressive Index Of. So we instead treat the searched argument as a list of major cells. Then we decide to search for cells of the other argument that have the same rank as those cells, since only cells with the same rank can match. That's a little strange for Bins, where it still makes sense to compare cells of different ranks. Furthermore, the result of any search function is always an array. To search for a single element and get an plain number, you need something like <code><span class='Value'>list</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΎ</span><span class='Function'><</span><span class='Value'>elt</span></code>.</p> <h3 id="trigonometry">Trigonometry</h3> <p>There are a lot of standard functions and I don't want to use separate primitives or a menu-style primitive like APL Circle for them. You can define all the functions eventually if you use complex exponential and take real and imaginary parts and inverses, but this doesn't sound well-suited for implementation. And there should be a math library that gives you the standard functions with normal names, but how will it be implemented?</p> <h3 id="right-to-left-multi-line-functions-go-upwards">Right-to-left multi-line functions go upwards</h3> @@ -30,9 +30,9 @@ <h3 id="subtraction--division--and-span-are-backwards">Subtraction, division, and span are backwards</h3> <p>The left argument feels much more like the primary one in these cases (indeed, this matches the typical left-to-right ordering of binary operators in mathematics). Not really fixable; too much precedent.</p> <h3 id="can-t-access-array-ordering-directly">Can't access array ordering directly</h3> -<p>Only <code><span class='Function'>ββ</span></code> use array ordering rather than just array comparison or numeric ordering. Getting at the actual ordering to just compare two arrays is more difficult than it should be (but not <em>that</em> difficult: <code><span class='Function'>β₯</span><span class='Composition'>βΈ</span><span class='Function'>β</span><span class='Composition'>βΎ</span><span class='Function'><</span></code> is TAO <code><span class='Function'>β€</span></code>).</p> +<p>Only <code><span class='Function'>ββ</span></code> use array ordering rather than just array equality or numeric ordering. Getting at the actual ordering to just compare two arrays is more difficult than it should be (but not <em>that</em> difficult: <code><span class='Function'>β₯</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Modifier2'>βΎ</span><span class='Function'><</span></code> is TAO <code><span class='Function'>β€</span></code>).</p> <h3 id="syntactic-type-erasure">Syntactic type erasure</h3> -<p>A programmer can call an operator on either a syntactic function or value, but there's no way to know within the operator which syntax that operand had. Maybe this is a better design, but it doesn't feel quite right that <code><span class='Value'>f</span><span class='Modifier'>Λ</span></code> is <code><span class='Value'>f</span></code>-Swap if <code><span class='Value'>f</span></code> has a function value. The array syntax suggest it should be Constant.</p> +<p>A programmer can call a modifier on either a syntactic function or subject, but there's no way to know within the modifier which syntax that operand had. Maybe this is a better design, but it doesn't feel quite right that <code><span class='Value'>f</span><span class='Modifier'>Λ</span></code> is <code><span class='Value'>f</span></code>-Swap if <code><span class='Value'>f</span></code> has a function value. The subject syntax suggests it should be Constant.</p> <h3 id="comparison-tolerance">Comparison tolerance</h3> <p>Kind of necessary for practical programming, but how should it be invoked or controlled? A system variable like <code><span class='Value'>β</span><span class='Function'>CT</span></code>? Per-primitive control? Both? Which primitives should use it?</p> <table> @@ -54,22 +54,22 @@ <h3 id="high-rank-array-notation">High-rank array notation</h3> <p>The proposed Dyalog array notation <code><span class='Value'>[]</span></code> for high-rank arrays: it's the same as BQN's lists <code><span class='Bracket'>β¨β©</span></code> except it mixes at the end. This works visually because the bottom levelβrowsβis written with stranding. It also looks okay with BQN strands but clashes with BQN lists. At that point it becomes apparent that specifying whether something is a high-rank array at the top axes is kind of strange: shouldn't it be the lower axes saying to combine with higher ones?</p> <h3 id="poor-font-support-">Poor font support</h3> -<p>Characters <code><span class='Function'>β₯βΎ</span><span class='Composition'>βββ</span><span class='Modifier'>Λ</span></code> and double-struck letters are either missing from many fonts or drawn strangely.</p> +<p>Characters <code><span class='Function'>β₯βΎ</span><span class='Modifier2'>βββ</span><span class='Modifier'>Λ</span></code> and double-struck letters are either missing from many fonts or drawn strangely.</p> <h3 id="index-of-privileges-the-first-match">Index Of privileges the first match</h3> -<p>It could be more sound to look at all matches, but using just the first one is too convenient. J has an index-of-last function; in BQN you have to reverse the left argument and then do arithmetic: <code><span class='Function'>β </span><span class='Composition'>β</span><span class='Function'>β£-</span><span class='Number'>1</span><span class='Function'>+β½</span><span class='Composition'>βΈ</span><span class='Function'>β</span></code>.</p> +<p>It could be more sound to look at all matches, but using just the first one is too convenient. J has an index-of-last function; in BQN you have to reverse the left argument and then do arithmetic: <code><span class='Function'>β </span><span class='Modifier2'>β</span><span class='Function'>β£-</span><span class='Number'>1</span><span class='Function'>+β½</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>.</p> <h3 id="glyphs-that-aren-t-great">Glyphs that aren't great</h3> -<p>Blanket issue for glyphs that need work. Currently I find <code><span class='Function'>β₯βββββ·</span><span class='Modifier'>βΌ</span><span class='Composition'>ββ</span></code> to not be particularly good fits for what they describe.</p> +<p>Blanket issue for glyphs that need work. Currently I find <code><span class='Function'>β₯βββββ·</span><span class='Modifier'>βΌ</span><span class='Modifier2'>ββ</span></code> to not be particularly good fits for what they describe.</p> <h3 id="axis-ordering-is-big-endian">Axis ordering is big-endian</h3> -<p>The most natural ordering for polynomial coefficients and base representations is little-endian, because it aligns element <code><span class='Value'>i</span></code> of the list with power <code><span class='Value'>i</span></code> of the argument or base. It also allows a forward scan instead of a reverse one. Array axes go the other way. However, there are advantages to this ordering as well. For example, it's common to act only on the first few axes, so having them at the beginning of the array is good (<code><span class='Function'>β </span><span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Function'>β</span><span class='Composition'>β</span><span class='Function'>β’</span><span class='Value'>a</span></code>).</p> +<p>The most natural ordering for polynomial coefficients and base representations is little-endian, because it aligns element <code><span class='Value'>i</span></code> of the list with power <code><span class='Value'>i</span></code> of the argument or base. It also allows a forward scan instead of a reverse one. Array axes go the other way. However, there are advantages to this ordering as well. For example, it's common to act only on the first few axes, so having them at the beginning of the array is good (<code><span class='Function'>β </span><span class='Value'>a</span> <span class='Gets'>ββ</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Function'>β’</span><span class='Value'>a</span></code>).</p> <h3 id="trains-don-t-like-monads">Trains don't like monads</h3> -<p>If you have the normal mix of monads and dyads you'll need a lot of parentheses and might end up abusing <code><span class='Composition'>β</span></code>. Largely solved with the "nothing" glyph <code><span class='Nothing'>Β·</span></code>, which acts like J's Cap (<code><span class='Value'>[:</span></code>) in a train, but still a minor frustration.</p> +<p>If you have the normal mix of monads and dyads you'll need a lot of parentheses and might end up abusing <code><span class='Modifier2'>β</span></code>. Largely solved with the "nothing" glyph <code><span class='Nothing'>Β·</span></code>, which acts like J's Cap (<code><span class='Value'>[:</span></code>) in a train, but still a minor frustration.</p> <h3 id="inverse-is-not-fully-specified">Inverse is not fully specified</h3> <p>So it seems a bit strange to rely on it for core language features like <code><span class='Function'>/</span><span class='Modifier'>βΌ</span></code>. On the other hand, this is a good fit for <code><span class='Function'>β</span><span class='Modifier'>βΌ</span></code> since we are taking an arbitrary branch of a complex function that has many of them. I'm pretty sure it's impossible to solve the issue as stated but it might be possible to move to less hazardous constructs. Structural Under is a start.</p> <h3 id="monadic-----versus----">Monadic <code><span class='Function'>β</span></code> versus <code><span class='Function'>></span></code></h3> <p>Both pull out elements and reduce the depth. But they face in opposite directions.</p> <p>The directions of <code><span class='Function'>ββ</span></code> and so on were mainly chosen to line up with <code><span class='Function'>β</span></code>: the argument that indices apply to (that is, the one that is searched or selected from) corresponds to the open side of the function. I'd probably prefer new glyphs that don't have this sort of directionality, however.</p> -<h3 id="converting-a-function-expression-to-a-syntactic-value-is-tricky">Converting a function expression to a syntactic value is tricky</h3> -<p>You can name it, you can write <code><span class='Function'>β</span><span class='Bracket'>β¨</span><span class='Function'>Expr</span><span class='Bracket'>β©</span></code>, and if it doesn't use special names you can write <code><span class='Brace'>{</span><span class='Function'>Expr</span><span class='Brace'>}</span></code>. All of these are at least a little awkward in reasonable cases. Should there be a dedicated syntax? Note that going the other way, from value to function, isn't too bad: the operator <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code> does it.</p> +<h3 id="converting-a-function-expression-to-a-subject-is-tricky">Converting a function expression to a subject is tricky</h3> +<p>You can name it, you can write <code><span class='Function'>β</span><span class='Bracket'>β¨</span><span class='Function'>Expr</span><span class='Bracket'>β©</span></code>, and if it doesn't use special names you can write <code><span class='Brace'>{</span><span class='Function'>Expr</span><span class='Brace'>}</span></code>. All of these are at least a little awkward in reasonable cases. Should there be a dedicated syntax? Note that going the other way, from subject to function, isn't too bad: the modifier <code><span class='Brace'>{</span><span class='Function'>π½</span><span class='Brace'>}</span></code> does it.</p> <h3 id="monadic-argument-corresponds-to-left-for-----and----">Monadic argument corresponds to left for <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code></h3> <p>Called dyadically, both functions shuffle cells of the right argument around, which is consistent with other selection-type functions. But the monadic case applies to what would be the left argument in the dyadic case.</p> <h3 id="prefixes-suffixes-add-depth-and-windows-doesn-t">Prefixes/Suffixes add depth and Windows doesn't</h3> @@ -84,7 +84,7 @@ <p>Boolean And (<code><span class='Function'>β§</span></code>) and Or (<code><span class='Function'>β¨</span></code>) are identical to Min (<code><span class='Function'>β</span></code>) and Max (<code><span class='Function'>β</span></code>) when restricted to Boolean arguments, and this would fit nicely with their monadic role as sorting functions: for example <code><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>b</span> <span class='Gets'>ββ</span> <span class='Function'>ββ§</span><span class='Value'>a</span><span class='Ligature'>βΏ</span><span class='Value'>b</span></code>. Furthermore the pairing of Min with Floor and Max with Ceiling is mnemonic only and not especially natural. The reason I have not used these glyphs for Min and Max, and have instead extended them to the somewhat superfluous <a href="doc/logic.html">arithmetic logical functions</a> is that Min and Max have different <a href="https://aplwiki.com/wiki/Identity_element">identity elements</a> of <code><span class='Number'>β</span></code> and <code><span class='Number'>Β―β</span></code> rather than <code><span class='Number'>1</span></code> and <code><span class='Number'>0</span></code>. Having to code around empty arrays when using <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span></code> would be a fairly big issue.</p> <p>The other drawback of Min (<code><span class='Function'>β§</span></code>) and Max (<code><span class='Function'>β¨</span></code>) is that the symbols are counterintuitive, but I have found a way to remember them: consider the graph of variables <code><span class='Value'>a</span><span class='Gets'>β</span><span class='Value'>x</span></code> and <code><span class='Value'>b</span><span class='Gets'>β</span><span class='Function'>Β¬</span><span class='Value'>x</span></code> for x from 0 to 1: two crossed lines. Now the graph of <code><span class='Value'>a</span><span class='Function'>β§</span><span class='Value'>b</span></code> is a caret shape and <code><span class='Value'>a</span><span class='Function'>β¨</span><span class='Value'>b</span></code> is a vee.</p> <h3 id="acting-on-windows-can-be-awkward">Acting on windows can be awkward</h3> -<p>When taking Windows along more than one axis, acting on the resulting array requires the Rank operator, duplicating either the right argument rank or (negated) left argument length. A nested Windows would only require Each.</p> +<p>When taking Windows along more than one axis, acting on the resulting array requires the Rank modifier, duplicating either the right argument rank or (negated) left argument length. A nested Windows would only require Each.</p> <h3 id="group-doesn-t-include-trailing-empty-groups">Group doesn't include trailing empty groups</h3> <p>But there are workarounds, described in <a href="doc/group.html">its documentation</a>. dzaima has suggested allowing a single extra element in the index argument to specify the result shape. Another possibility is for the result prototype to be specified to allow overtaking.</p> <h3 id="scan-ordering-is-weird">Scan ordering is weird</h3> @@ -97,14 +97,14 @@ <p>Select chooses whether the left argument maps to right argument axes or selects from the first axis only based on its depth. Without prototypes an empty array has depth 1, so it selects no major cells. However, it could also select from no axes (a no-op) and in some contexts the other behavior would be surprising.</p> <h3 id="unclear-primitive-names">Unclear primitive names</h3> <p>Blanket issue for names that I don't find informative: "Solo", "Bins", "Unique Mask", "Find", and "Group".</p> -<h3 id="-modifier--and--composition--terminology">"Modifier" and "composition" terminology</h3> -<p>Since the two have different syntax, it's important that they have completely separate names. "Modifier" is good but "composition" is not, as it doesn't seem to fit with Rank, Depth, or Iterate. There's also often a need for a term that refers to both. I've been using "operator" but that can be confusing.</p> <h3 id="should-have-a-rounding-function">Should have a rounding function</h3> <p>There is a standard way to round floatsβto nearest integer, ties to evenβbut it's fairly hard to implement and would have to be specially recognized for performance. It would be nice to have a better way to access this.</p> <h3 id="primitive-name-capitalization">Primitive name capitalization</h3> <p>I went with "Index of" and "Less Than or Equal to" but the last word blends into surrounding text. Should they be fully capitalized or hyphenated?</p> <h2 id="solved-problems">Solved problems</h2> <p>Problems that existed in mainstream APL or a transitional BQN that have in my opinion been put to rest (while in some cases introducing new problems). Listed in reverse chronological order by time solved, by my recollection.</p> +<h3 id="-modifier--and--composition--terminology">"Modifier" and "composition" terminology</h3> +<p>1-modifiers and 2-modifiers used to be called "modifiers" and "compositions", respectively, and sometimes "operators" collectively. The new names are much better, although they do leave a disconnect between the names for modifiers, and those for their inputsβ"operands".</p> <h3 id="can-t-return-from-inner-functions">Can't return from inner functions</h3> <p>Fixed by adding block returns such as <code><span class='Value'>label</span><span class='Gets'>β</span></code> to jump out of a block with header name <code><span class='Value'>label</span></code>. Hopefully these don't cause too many new problems.</p> <p>This was an issue with using functions as control flow. For example, when looping through an array with Each, you can't decide to exit early. In a curly-brace language you would just use a for loop and a return. In BQN, we needβ¦ longjmp? Maybe not as crazy as it sounds, and potentially worth it in exchange for replacing control structures.</p> diff --git a/docs/running.html b/docs/running.html index 9e30492d..02ad00db 100644 --- a/docs/running.html +++ b/docs/running.html @@ -2,7 +2,7 @@ <h1 id="how-to-run-bqn">How to run BQN</h1> <p>BQN is in a very early stage of development, and there is currently no complete implementation of the language. However, it's a relatively simple language to implement, and a few implementations come close.</p> <h3 id="bqn-ngn">BQN2NGN</h3> -<p><a href="https://github.com/mlochbaum/BQN2NGN">BQN2NGN</a> is a prototype implementation in Javascript build to experiment with the langauge, which is now abandoned. Because you can <a href="https://mlochbaum.github.io/BQN2NGN/web/index.html">use it online</a>, this is probably the quickest way to get started with BQN. It has good primitive support, with the main issues being that it uses a J-style insert instead of BQN-style vector reduction, that it has a different version of <a href="doc/group.html">Group</a> (<code><span class='Function'>β</span></code>), and that it is missing Choose (<code><span class='Composition'>βΆ</span></code>). There are also some spelling differences, with Deduplicate (<code><span class='Function'>β·</span></code>) spelled with <code><span class='Value'>βͺ</span></code> and Valences (<code><span class='Composition'>β</span></code>) spelled with <code><span class='Value'>β </span></code>. It is missing value blocks and function headers.</p> +<p><a href="https://github.com/mlochbaum/BQN2NGN">BQN2NGN</a> is a prototype implementation in Javascript build to experiment with the langauge, which is now abandoned. Because you can <a href="https://mlochbaum.github.io/BQN2NGN/web/index.html">use it online</a>, this is probably the quickest way to get started with BQN. It has good primitive support, with the main issues being that it uses a J-style insert instead of BQN-style vector reduction, that it has a different version of <a href="doc/group.html">Group</a> (<code><span class='Function'>β</span></code>), and that it is missing Choose (<code><span class='Modifier2'>βΆ</span></code>). There are also some spelling differences, with Deduplicate (<code><span class='Function'>β·</span></code>) spelled with <code><span class='Value'>βͺ</span></code> and Valences (<code><span class='Modifier2'>β</span></code>) spelled with <code><span class='Value'>β </span></code>. It is missing value blocks and function headers.</p> <p>For automated testing I run BQN2NGN using the <code><span class='Value'>bqn</span></code> executable, which is just a symlink to <code><span class='Value'>apl.js</span></code> in the BQN2NGN repository. It requires Node to run.</p> <h3 id="dzaima-bqn">dzaima/BQN</h3> <p><a href="https://github.com/dzaima/BQN/">dzaima/BQN</a> is an implementation in Java created by modifying the existing dzaima/APL. It should be easy to run on desktop Linux and Android. It is still in development and has almost complete syntax support but incomplete primitive support.</p> diff --git a/docs/spec/evaluate.html b/docs/spec/evaluate.html index 6552f80e..7ebbe147 100644 --- a/docs/spec/evaluate.html +++ b/docs/spec/evaluate.html @@ -4,16 +4,16 @@ <h3 id="programs-and-blocks">Programs and blocks</h3> <p>The result of parsing a valid BQN program is a <code><span class='Function'>PROGRAM</span></code>, and the program is run by evaluating this term.</p> <p>A <code><span class='Function'>PROGRAM</span></code> or <code><span class='Function'>BODY</span></code> is a list of <code><span class='Function'>STMT</span></code>s (for <code><span class='Function'>BODY</span></code>, the last must be an <code><span class='Function'>EXPR</span></code>, a particular kind of <code><span class='Function'>STMT</span></code>), which are evaluated in program order. The statement <code><span class='Value'>nothing</span></code> does nothing when evaluated, while <code><span class='Function'>EXPR</span></code> evaluates some APL code and possibly assigns the results, as described below.</p> -<p>A block consists of several <code><span class='Function'>BODY</span></code> terms, some of which may have an accompanying header describing accepted inputs and how they are processed. A value block <code><span class='Value'>brVal</span></code> can only have one <code><span class='Function'>BODY</span></code>, and is evaluated by evaluating the code in it. Other types of blocks do not evaluate any <code><span class='Function'>BODY</span></code> immediately, but instead return a function, modifier, or operator that obtains its result by evaluating a particular <code><span class='Function'>BODY</span></code>. The <code><span class='Function'>BODY</span></code> is identified and evaluated once the block has received enough inputs (operands or arguments), which for modifiers and compositions can take one or two calls: if two calls are required, then on the first call the operands are simply stored and no code is evaluated yet. Two calls are required if there is more than one <code><span class='Function'>BODY</span></code> term, if the <code><span class='Function'>BODY</span></code> contains the special names <code><span class='Value'>π¨π©π€</span><span class='Function'>πππ</span></code>, or if its header specifies arguments (the header-body is a <code><span class='Modifier'>_mCase</span></code> or <code><span class='Composition'>_cCase_</span></code>). Otherwise only one is required.</p> -<p>To evaluate a block when enough inputs have been received, first the correct case must be identified. To do this, first each special case (<code><span class='Function'>FCase</span></code>, <code><span class='Modifier'>_mCase</span></code>, or <code><span class='Composition'>_cCase_</span></code>) is checked in order to see if its arguments are strucurally compatible with the given arguments. That is, is <code><span class='Value'>headW</span></code> is a <code><span class='Value'>value</span></code>, there must be a left argument matching that structure, and if <code><span class='Value'>headX</span></code> is a <code><span class='Value'>value</span></code>, the right argument must match that structure. This means that <code><span class='Value'>π¨</span></code> not only matches any left argument but also no argument. The test for compatibility is the same as for multiple assignment described below, except that the header may contain constants, which must match the corresponding part of the given argument.If no special case matches, then an appropriate general case (<code><span class='Function'>FMain</span></code>, <code><span class='Modifier'>_mMain</span></code>, or <code><span class='Composition'>_cMain_</span></code>) is used: if there are two, the first is used with no left argument and the second with a left argument; if there are one, it is always used, and if there are none, an error results.</p> -<p>The only remaining step before evaluating the <code><span class='Function'>BODY</span></code> is to bind the inputs and other names. Special names are always bound when applicable: <code><span class='Value'>π¨π©π€</span></code> if arguments are used, <code><span class='Value'>π¨</span></code> if there is a left argument, <code><span class='Value'>ππ</span></code> if operands are used, and <code><span class='Composition'>_</span><span class='Value'>π£</span></code> and <code><span class='Composition'>_</span><span class='Value'>π£</span><span class='Composition'>_</span></code> for modifiers and combinators, respectively. Any names in the header are also bound, allowing multiple assignment for arguments.</p> +<p>A block consists of several <code><span class='Function'>BODY</span></code> terms, some of which may have an accompanying header describing accepted inputs and how they are processed. An immediate block <code><span class='Value'>brImm</span></code> can only have one <code><span class='Function'>BODY</span></code>, and is evaluated by evaluating the code in it. Other types of blocks do not evaluate any <code><span class='Function'>BODY</span></code> immediately, but instead return a function or modifier that obtains its result by evaluating a particular <code><span class='Function'>BODY</span></code>. The <code><span class='Function'>BODY</span></code> is identified and evaluated once the block has received enough inputs (operands or arguments), which for modifiers can take one or two calls: if two calls are required, then on the first call the operands are simply stored and no code is evaluated yet. Two calls are required if there is more than one <code><span class='Function'>BODY</span></code> term, if the <code><span class='Function'>BODY</span></code> contains the special names <code><span class='Value'>π¨π©π€</span><span class='Function'>πππ</span></code>, or if its header specifies arguments (the header-body combination is a <code><span class='Modifier'>_mCase</span></code> or <code><span class='Modifier2'>_cCase_</span></code>). Otherwise only one is required.</p> +<p>To evaluate a block when enough inputs have been received, first the correct case must be identified. To do this, first each special case (<code><span class='Function'>FCase</span></code>, <code><span class='Modifier'>_mCase</span></code>, or <code><span class='Modifier2'>_cCase_</span></code>) is checked in order to see if its arguments are strucurally compatible with the given arguments. That is, is <code><span class='Value'>headW</span></code> is a <code><span class='Value'>subject</span></code>, there must be a left argument matching that structure, and if <code><span class='Value'>headX</span></code> is a <code><span class='Value'>subject</span></code>, the right argument must match that structure. This means that <code><span class='Value'>π¨</span></code> not only matches any left argument but also no argument. The test for compatibility is the same as for multiple assignment described below, except that the header may contain constants, which must match the corresponding part of the given argument.If no special case matches, then an appropriate general case (<code><span class='Function'>FMain</span></code>, <code><span class='Modifier'>_mMain</span></code>, or <code><span class='Modifier2'>_cMain_</span></code>) is used: if there are two, the first is used with no left argument and the second with a left argument; if there are one, it is always used, and if there are none, an error results.</p> +<p>The only remaining step before evaluating the <code><span class='Function'>BODY</span></code> is to bind the inputs and other names. Special names are always bound when applicable: <code><span class='Value'>π¨π©π€</span></code> if arguments are used, <code><span class='Value'>π¨</span></code> if there is a left argument, <code><span class='Value'>ππ</span></code> if operands are used, and <code><span class='Modifier2'>_</span><span class='Value'>π£</span></code> and <code><span class='Modifier2'>_</span><span class='Value'>π£</span><span class='Modifier2'>_</span></code> for modifiers and combinators, respectively. Any names in the header are also bound, allowing multiple assignment for arguments.</p> <p>If there is no left argument, but the <code><span class='Function'>BODY</span></code> contains <code><span class='Value'>π¨</span></code> at the top level, then it is conceptually re-parsed with <code><span class='Value'>π¨</span></code> replaced by <code><span class='Nothing'>Β·</span></code> to give a monadic version before application. As the only effect when this re-parsed form is valid is to change some instances of <code><span class='Value'>arg</span></code> to <code><span class='Value'>nothing</span></code>, this can be achieved efficiently by annotating parts of the AST that depend on <code><span class='Value'>π¨</span></code> as conditionally-nothing. However, it also causes an error if <code><span class='Value'>π¨</span></code> is used as an operand or list element, where <code><span class='Value'>nothing</span></code> is not allowed by the grammar.</p> <h3 id="assignment">Assignment</h3> -<p>An <em>assignment</em> is one of the four rules containing <code><span class='Function'>ASGN</span></code>. It is evaluated by first evaluating the right-hand-side <code><span class='Value'>valExpr</span></code>, <code><span class='Function'>FuncExpr</span></code>, <code><span class='Modifier'>_modExpr</span></code>, or <code><span class='Composition'>_cmpExp_</span></code> expression, and then storing the result in the left-hand-side identifier or identifiers. The result of the assignment expression is the result of its right-hand side. Except for values, only a lone identifier is allowed on the left-hand side and storage is obvious. For values, <em>multiple assignment</em> with a list left-hand side is also allowed. Multiple assignment is performed recursively by assigning right-hand-side values to the left-hand-side targets, with single-identifier (<code><span class='Value'>v</span></code>) assignment as the base case. When matching the right-hand side to a list left-hand side, the left hand side is treated as a list of <code><span class='Value'>lhs</span></code> targets. The evaluated right-hand side must be a list (rank-1 array) of the same length, and is matched to these targets element-wise.</p> -<p><em>Modified assignment</em> is the value assignment rule <code><span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='String'>"β©"</span> <span class='Value'>valExpr</span></code>. In this case, <code><span class='Value'>lhs</span></code> should be evaluated as if it were a <code><span class='Value'>valExpr</span></code> (the syntax is a subset of <code><span class='Value'>valExpr</span></code>), and the result of the function application <code><span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='Value'>valExpr</span></code> should be assigned to <code><span class='Value'>lhs</span></code>, and is also the result of the modified assignment expression.</p> +<p>An <em>assignment</em> is one of the four rules containing <code><span class='Function'>ASGN</span></code>. It is evaluated by first evaluating the right-hand-side <code><span class='Value'>subExpr</span></code>, <code><span class='Function'>FuncExpr</span></code>, <code><span class='Modifier'>_m1Expr</span></code>, or <code><span class='Modifier2'>_m2Exp_</span></code> expression, and then storing the result in the left-hand-side identifier or identifiers. The result of the assignment expression is the result of its right-hand side. Except for subjects, only a lone identifier is allowed on the left-hand side and storage is obvious. For subjects, <em>multiple assignment</em> with a list left-hand side is also allowed. Multiple assignment is performed recursively by assigning right-hand-side values to the left-hand-side targets, with single-identifier (<code><span class='Value'>s</span></code>) assignment as the base case. When matching the right-hand side to a list left-hand side, the left hand side is treated as a list of <code><span class='Value'>lhs</span></code> targets. The evaluated right-hand side must be a list (rank-1 array) of the same length, and is matched to these targets element-wise.</p> +<p><em>Modified assignment</em> is the subject assignment rule <code><span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='String'>"β©"</span> <span class='Value'>subExpr</span></code>. In this case, <code><span class='Value'>lhs</span></code> should be evaluated as if it were a <code><span class='Value'>subExpr</span></code> (the syntax is a subset of <code><span class='Value'>subExpr</span></code>), and the result of the function application <code><span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='Value'>subExpr</span></code> should be assigned to <code><span class='Value'>lhs</span></code>, and is also the result of the modified assignment expression.</p> <h3 id="expressions">Expressions</h3> -<p>We now give rules for evaluating an <code><span class='Value'>atom</span></code>, <code><span class='Function'>Func</span></code>, <code><span class='Modifier'>_mod</span></code> or <code><span class='Composition'>_comp_</span></code> expression (the possible options for <code><span class='Function'>ANY</span></code>). A literal <code><span class='Value'>vl</span></code>, <code><span class='Function'>Fl</span></code>, <code><span class='Modifier'>_ml</span></code>, or <code><span class='Composition'>_cl_</span></code> has a fixed value defined by the specification (<a href="literal.html">value literals</a> and <a href="primitive.html">built-ins</a>). An identifier <code><span class='Value'>v</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, or <code><span class='Composition'>_c_</span></code> is evaluated by returning its value; because of the scoping rules it must have one when evaluated. A parenthesized expression such as <code><span class='String'>"("</span> <span class='Modifier'>_modExpr</span> <span class='String'>")"</span></code> simply returns the result of the interior expression. A braced construct such as <code><span class='Function'>BraceFunc</span></code> is defined by the evaluation of the statements it contains after all parameters are accepted. Finally, a list <code><span class='String'>"β¨"</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='String'>"β©"</span></code> or <code><span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span></code> consists grammatically of a list of expressions. To evaluate it, each expression is evaluated in source order and their results are placed as elements of a rank-1 array. The two forms have identical semantics but different punctuation.</p> -<p>Rules in the table below are function and operator evaluation.</p> +<p>We now give rules for evaluating an <code><span class='Value'>atom</span></code>, <code><span class='Function'>Func</span></code>, <code><span class='Modifier'>_mod1</span></code> or <code><span class='Modifier2'>_mod2_</span></code> expression (the possible options for <code><span class='Function'>ANY</span></code>). A literal or primitive <code><span class='Value'>sl</span></code>, <code><span class='Function'>Fl</span></code>, <code><span class='Modifier'>_ml</span></code>, or <code><span class='Modifier2'>_cl_</span></code> has a fixed value defined by the specification (<a href="literal.html">literals</a> and <a href="primitive.html">built-ins</a>). An identifier <code><span class='Value'>s</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, or <code><span class='Modifier2'>_c_</span></code> is evaluated by returning its value; because of the scoping rules it must have one when evaluated. A parenthesized expression such as <code><span class='String'>"("</span> <span class='Modifier'>_modExpr</span> <span class='String'>")"</span></code> simply returns the result of the interior expression. A braced construct such as <code><span class='Function'>BraceFunc</span></code> is defined by the evaluation of the statements it contains after all parameters are accepted. Finally, a list <code><span class='String'>"β¨"</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='String'>"β©"</span></code> or <code><span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span></code> consists grammatically of a list of expressions. To evaluate it, each expression is evaluated in source order and their results are placed as elements of a rank-1 array. The two forms have identical semantics but different punctuation.</p> +<p>Rules in the table below are function and modifier evaluation.</p> <table> <thead> <tr> @@ -28,32 +28,32 @@ <tbody> <tr> <td><code><span class='Value'>π¨</span></code></td> -<td><code><span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span></code></td> +<td><code><span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span></code></td> <td><code><span class='Function'>Derv</span></code></td> <td><code><span class='Value'>arg</span></code></td> <td><code><span class='Value'>π©</span></code></td> -<td>Function, value</td> +<td>Function, subject</td> </tr> <tr> <td><code><span class='Value'>π</span></code></td> <td><code><span class='Function'>Operand</span></code></td> -<td><code><span class='Modifier'>_mod</span></code></td> +<td><code><span class='Modifier'>_mod1</span></code></td> <td></td> <td></td> -<td>Modifier</td> +<td>1-Modifier</td> </tr> <tr> <td><code><span class='Value'>π</span></code></td> <td><code><span class='Function'>Operand</span></code></td> -<td><code><span class='Composition'>_comp_</span></code></td> -<td><code><span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span></code></td> +<td><code><span class='Modifier2'>_mod2_</span></code></td> +<td><code><span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span></code></td> <td><code><span class='Value'>π</span></code></td> -<td>Composition</td> +<td>2-Modifier</td> </tr> </tbody> </table> -<p>In each case the constituent expressions are evaluated in reverse source order: Right, then Called, then Left. Then the expression's result is obtained by calling the Called value on its parameters. A left argument of <code><span class='Value'>nothing</span></code> is not used as a parameter, leaving only a right argument in that case. The data type of the Called value must be appropriate to the expression type, as indicated in the "Types" column. For function application, a value type (number, character, or array) is allowed. It is called simply by returning itself. Although the arguments are ignored in this case, they are still evaluated. A braced construct is evaluated by binding the parameter names given in columns L and R to the corresponding values. Then if all parameter levels present have been bound, its body is evaluated to give the result of application.</p> -<p>The following rules derive new functions or operators from existing ones.</p> +<p>In each case the constituent expressions are evaluated in reverse source order: Right, then Called, then Left. Then the expression's result is obtained by calling the Called value on its parameters. A left argument of <code><span class='Value'>nothing</span></code> is not used as a parameter, leaving only a right argument in that case. The type of the Called value must be appropriate to the expression type, as indicated in the "Types" column. For function application, a data type (number, character, or array) is allowed. It is called simply by returning itself. Although the arguments are ignored in this case, they are still evaluated. A braced construct is evaluated by binding the parameter names given in columns L and R to the corresponding values. Then if all parameter levels present have been bound, its body is evaluated to give the result of application.</p> +<p>The following rules derive new functions or modifiers from existing ones.</p> <table> <thead> <tr> @@ -66,15 +66,15 @@ <tbody> <tr> <td></td> -<td><code><span class='Composition'>_comp_</span></code></td> -<td><code><span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span></code></td> -<td><code><span class='Brace'>{</span><span class='Function'>π½</span> <span class='Composition'>_C_</span> <span class='Function'>R</span><span class='Brace'>}</span></code></td> +<td><code><span class='Modifier2'>_mod2_</span></code></td> +<td><code><span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span></code></td> +<td><code><span class='Brace'>{</span><span class='Function'>π½</span> <span class='Modifier2'>_C_</span> <span class='Function'>R</span><span class='Brace'>}</span></code></td> </tr> <tr> <td><code><span class='Function'>Operand</span></code></td> -<td><code><span class='Composition'>_comp_</span></code></td> +<td><code><span class='Modifier2'>_mod2_</span></code></td> <td></td> -<td><code><span class='Brace'>{</span><span class='Function'>L</span> <span class='Composition'>_C_</span> <span class='Function'>π½</span><span class='Brace'>}</span></code></td> +<td><code><span class='Brace'>{</span><span class='Function'>L</span> <span class='Modifier2'>_C_</span> <span class='Function'>π½</span><span class='Brace'>}</span></code></td> </tr> <tr> <td><code><span class='Function'>Operand</span></code></td> @@ -90,5 +90,5 @@ </tr> </tbody> </table> -<p>As with applications, all expressions are evaluated in reverse source order before doing anything else. Then a result is formed without calling the center value. Its value in BQN is given in the rightmost column, using <code><span class='Function'>L</span></code>, <code><span class='Function'>C</span></code>, and <code><span class='Function'>R</span></code> for the results of the expressions in the left, center, and right columns, respectively. For the first two rules (<em>partial application</em>), the given operand is bound to the composition: the result is a modifier that, when called, calls the center composition with the bound operand on the same side it appeared on and the new operand on the remaining side. A <em>train</em> is a function that, when called, calls the right-hand function on all arguments, then the left-hand function, and calls the center function with these results as arguments. In a composition partial application, the result will fail when applied if the center value does not have the composition type, and in a fork, it will fail if any component has a modifier or composition type (that is, cannot be applied as a function). BQN implementations are not required to check for these types when forming the result of these expressions, but may give an error on formation even if the result will never be applied.</p> +<p>As with applications, all expressions are evaluated in reverse source order before doing anything else. Then a result is formed without calling the center value. Its value in BQN is given in the rightmost column, using <code><span class='Function'>L</span></code>, <code><span class='Function'>C</span></code>, and <code><span class='Function'>R</span></code> for the results of the expressions in the left, center, and right columns, respectively. For the first two rules (<em>partial application</em>), the given operand is bound to the 2-modifier: the result is a 1-modifier that, when called, calls the center 2-modifier with the bound operand on the same side it appeared on and the new operand on the remaining side. A <em>train</em> is a function that, when called, calls the right-hand function on all arguments, then the left-hand function, and calls the center function with these results as arguments. In a modifier partial application, the result will fail when applied if the center value does not have the 2-modifier type, and in a fork, it will fail if any component has a modifier type (that is, cannot be applied as a function). BQN implementations are not required to check for these types when forming the result of these expressions, but may give an error on formation even if the result will never be applied.</p> diff --git a/docs/spec/grammar.html b/docs/spec/grammar.html index 56803787..09d0bafa 100644 --- a/docs/spec/grammar.html +++ b/docs/spec/grammar.html @@ -1,35 +1,35 @@ <head><link href="../style.css" rel="stylesheet"/></head> <p>BQN's grammar is given below. Terms are defined in a <a href="https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form">BNF</a> variant. However, handling special names properly is possible but difficult in BNF, so they are explained in text along with the braced block grammar.</p> -<p>The symbols <code><span class='Value'>v</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, and <code><span class='Composition'>_c_</span></code> are identifier tokens with value, function, modifier, and composition classes respectively. Similarly, <code><span class='Value'>vl</span></code>, <code><span class='Function'>Fl</span></code>, <code><span class='Modifier'>_ml</span></code>, and <code><span class='Composition'>_cl_</span></code> refer to value literals (numeric and character literals, or primitives) of those classes. While names in the BNF here follow the identifier naming scheme, this is informative only: syntactic classes are no longer used after parsing and cannot be inspected in a running program.</p> +<p>The symbols <code><span class='Value'>s</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, and <code><span class='Modifier2'>_c_</span></code> are identifier tokens with subject, function, 1-modifier, and 2-modifier classes respectively. Similarly, <code><span class='Value'>sl</span></code>, <code><span class='Function'>Fl</span></code>, <code><span class='Modifier'>_ml</span></code>, and <code><span class='Modifier2'>_cl_</span></code> refer to literals and primitives of those classes. While names in the BNF here follow the identifier naming scheme, this is informative only: syntactic classes are no longer used after parsing and cannot be inspected in a running program.</p> <p>A program is a list of statements. Almost all statements are expressions. However, explicit definitions and valueless results stemming from <code><span class='Nothing'>Β·</span></code>, or <code><span class='Value'>π¨</span></code> in a monadic brace function, can be used as statements but not expressions.</p> <pre><span class='Function'>PROGRAM</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='Function'>STMT</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>STMT</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>STMT</span> <span class='Function'>=</span> <span class='Function'>EXPR</span> <span class='Function'>|</span> <span class='Function'>DEF</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Separator'>β</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='String'>"β"</span> <span class='Function'>|</span> <span class='String'>","</span> <span class='Function'>|</span> <span class='Value'>\n</span> <span class='Paren'>)</span><span class='Function'>+</span> -<span class='Function'>EXPR</span> <span class='Function'>=</span> <span class='Value'>valExpr</span> <span class='Function'>|</span> <span class='Function'>FuncExpr</span> <span class='Function'>|</span> <span class='Modifier'>_modExpr</span> <span class='Function'>|</span> <span class='Composition'>_cmpExp_</span> +<span class='Function'>EXPR</span> <span class='Function'>=</span> <span class='Value'>subExpr</span> <span class='Function'>|</span> <span class='Function'>FuncExpr</span> <span class='Function'>|</span> <span class='Modifier'>_m1Expr</span> <span class='Function'>|</span> <span class='Modifier2'>_m2Expr_</span> </pre> -<p>Here we define the "atomic" forms of functions and operators, which are either single tokens or enclosed in paired symbols. Stranded vectors with <code><span class='Ligature'>βΏ</span></code>, which binds more tightly than any form of execution, are also included.</p> -<pre><span class='Function'>ANY</span> <span class='Function'>=</span> <span class='Value'>atom</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Function'>|</span> <span class='Modifier'>_mod</span> <span class='Function'>|</span> <span class='Composition'>_comp_</span> -<span class='Composition'>_comp_</span> <span class='Function'>=</span> <span class='Composition'>_c_</span> <span class='Function'>|</span> <span class='Composition'>_cl_</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Composition'>_cmpExp_</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Composition'>_brComp_</span> -<span class='Modifier'>_mod</span> <span class='Function'>=</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Modifier'>_ml</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Modifier'>_modExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Modifier'>_brMod</span> -<span class='Function'>Func</span> <span class='Function'>=</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='Function'>Fl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Function'>FuncExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Function'>BrFunc</span> -<span class='Value'>atom</span> <span class='Function'>=</span> <span class='Value'>v</span> <span class='Function'>|</span> <span class='Value'>vl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Value'>valExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Value'>brVal</span> <span class='Function'>|</span> <span class='Value'>list</span> +<p>Here we define the "atomic" forms of functions and modifiers, which are either single tokens or enclosed in paired symbols. Stranded vectors with <code><span class='Ligature'>βΏ</span></code>, which binds more tightly than any form of execution, are also included.</p> +<pre><span class='Function'>ANY</span> <span class='Function'>=</span> <span class='Value'>atom</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Function'>|</span> <span class='Modifier'>_mod1</span> <span class='Function'>|</span> <span class='Modifier2'>_mod2_</span> +<span class='Modifier2'>_mod2_</span> <span class='Function'>=</span> <span class='Modifier2'>_c_</span> <span class='Function'>|</span> <span class='Modifier2'>_cl_</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Modifier2'>_m1Expr_</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Modifier2'>_brMod2_</span> +<span class='Modifier'>_mod1</span> <span class='Function'>=</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Modifier'>_ml</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Modifier'>_m2Expr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Modifier'>_brMod1</span> +<span class='Function'>Func</span> <span class='Function'>=</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='Function'>Fl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Function'>FuncExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Function'>BrFunc</span> +<span class='Value'>atom</span> <span class='Function'>=</span> <span class='Value'>s</span> <span class='Function'>|</span> <span class='Value'>sl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Value'>subExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Value'>brSub</span> <span class='Function'>|</span> <span class='Value'>list</span> <span class='Value'>list</span> <span class='Function'>=</span> <span class='String'>"β¨"</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='String'>"β©"</span> -<span class='Value'>value</span> <span class='Function'>=</span> <span class='Value'>atom</span> <span class='Function'>|</span> <span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span> +<span class='Value'>subject</span> <span class='Function'>=</span> <span class='Value'>atom</span> <span class='Function'>|</span> <span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span> </pre> -<p>Starting at the highest-order objects, modifiers and compositions have fairly simple syntax. In most cases the syntax for <code><span class='Gets'>β</span></code> and <code><span class='Gets'>β©</span></code> is the same, but only <code><span class='Gets'>β©</span></code> can be used for modified assignment.</p> +<p>Starting at the highest-order objects, modifiers have fairly simple syntax. In most cases the syntax for <code><span class='Gets'>β</span></code> and <code><span class='Gets'>β©</span></code> is the same, but only <code><span class='Gets'>β©</span></code> can be used for modified assignment.</p> <pre><span class='Function'>ASGN</span> <span class='Function'>=</span> <span class='String'>"β"</span> <span class='Function'>|</span> <span class='String'>"β©"</span> -<span class='Composition'>_cmpExp_</span> <span class='Function'>=</span> <span class='Composition'>_comp_</span> - <span class='Function'>|</span> <span class='Composition'>_c_</span> <span class='Function'>ASGN</span> <span class='Composition'>_cmpExp_</span> -<span class='Modifier'>_modExpr</span> <span class='Function'>=</span> <span class='Modifier'>_mod</span> - <span class='Function'>|</span> <span class='Composition'>_comp_</span> <span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span> <span class='Comment'># Right partial application -</span> <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Composition'>_comp_</span> <span class='Comment'># Left partial application -</span> <span class='Function'>|</span> <span class='Modifier'>_m</span> <span class='Function'>ASGN</span> <span class='Modifier'>_modExpr</span> +<span class='Modifier2'>_m2Expr_</span> <span class='Function'>=</span> <span class='Modifier2'>_mod2_</span> + <span class='Function'>|</span> <span class='Modifier2'>_c_</span> <span class='Function'>ASGN</span> <span class='Modifier2'>_m2Expr_</span> +<span class='Modifier'>_m1Expr</span> <span class='Function'>=</span> <span class='Modifier'>_mod1</span> + <span class='Function'>|</span> <span class='Modifier2'>_mod2_</span> <span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span> <span class='Comment'># Right partial application +</span> <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Modifier2'>_mod2_</span> <span class='Comment'># Left partial application +</span> <span class='Function'>|</span> <span class='Modifier'>_m</span> <span class='Function'>ASGN</span> <span class='Modifier'>_m1Expr</span> </pre> -<p>Functions can be formed by fully applying operators or as trains. Operators are left-associative, so that the left operand (<code><span class='Function'>Operand</span></code>) can include operators but the right operand (<code><span class='Value'>value</span> <span class='Function'>|</span> <span class='Function'>Func</span></code>) cannot. Trains are right-associative, but bind less tightly than operators. Assignment is not allowed in the top level of a train: it must be parenthesized.</p> +<p>Functions can be formed by fully applying modifiers or as trains. modifiers are left-associative, so that the left operand (<code><span class='Function'>Operand</span></code>) can include modifier applications but the right operand (<code><span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Func</span></code>) cannot. Trains are right-associative, but bind less tightly than modifiers. Assignment is not allowed in the top level of a train: it must be parenthesized.</p> <pre><span class='Function'>Derv</span> <span class='Function'>=</span> <span class='Function'>Func</span> - <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Modifier'>_mod</span> - <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Composition'>_comp_</span> <span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span> -<span class='Function'>Operand</span> <span class='Function'>=</span> <span class='Value'>value</span> + <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Modifier'>_mod1</span> + <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Modifier2'>_mod2_</span> <span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Func</span> <span class='Paren'>)</span> +<span class='Function'>Operand</span> <span class='Function'>=</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Function'>Derv</span> <span class='Function'>Fork</span> <span class='Function'>=</span> <span class='Function'>Derv</span> <span class='Function'>|</span> <span class='Function'>Operand</span> <span class='Function'>Derv</span> <span class='Function'>Fork</span> <span class='Comment'># 3-train @@ -39,63 +39,63 @@ </span><span class='Function'>FuncExpr</span> <span class='Function'>=</span> <span class='Function'>Train</span> <span class='Function'>|</span> <span class='Function'>F</span> <span class='Function'>ASGN</span> <span class='Function'>FuncExpr</span> </pre> -<p>Value expressions are complicated by the possibility of list assignment. We also define nothing-statements, which have very similar syntax to value expressions but do not permit assignment.</p> -<pre><span class='Value'>arg</span> <span class='Function'>=</span> <span class='Value'>valExpr</span> - <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>arg</span> +<p>Subject expressions are complicated by the possibility of list assignment. We also define nothing-statements, which have very similar syntax to subject expressions but do not permit assignment.</p> +<pre><span class='Value'>arg</span> <span class='Function'>=</span> <span class='Value'>subExpr</span> + <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>arg</span> <span class='Value'>nothing</span> <span class='Function'>=</span> <span class='String'>"Β·"</span> - <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>nothing</span> -<span class='Function'>LHS_ANY</span> <span class='Function'>=</span> <span class='Value'>lhsValue</span> <span class='Function'>|</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Composition'>_c_</span> + <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>nothing</span> +<span class='Function'>LHS_ANY</span> <span class='Function'>=</span> <span class='Value'>lhsSub</span> <span class='Function'>|</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Modifier2'>_c_</span> <span class='Function'>LHS_ATOM</span> <span class='Function'>=</span> <span class='Function'>LHS_ANY</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Value'>lhsStr</span> <span class='String'>")"</span> <span class='Function'>LHS_ELT</span> <span class='Function'>=</span> <span class='Function'>LHS_ANY</span> <span class='Function'>|</span> <span class='Value'>lhsStr</span> -<span class='Value'>lhsValue</span> <span class='Function'>=</span> <span class='Value'>v</span> +<span class='Value'>lhsSub</span> <span class='Function'>=</span> <span class='Value'>s</span> <span class='Function'>|</span> <span class='String'>"β¨"</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='Function'>LHS_ELT</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>LHS_ELT</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='String'>"β©"</span> <span class='Value'>lhsStr</span> <span class='Function'>=</span> <span class='Function'>LHS_ATOM</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>LHS_ATOM</span> <span class='Paren'>)</span><span class='Function'>+</span> -<span class='Value'>lhs</span> <span class='Function'>=</span> <span class='Value'>lhsValue</span> <span class='Function'>|</span> <span class='Value'>lhsStr</span> -<span class='Value'>valExpr</span> <span class='Function'>=</span> <span class='Value'>arg</span> - <span class='Function'>|</span> <span class='Value'>lhs</span> <span class='Function'>ASGN</span> <span class='Value'>valExpr</span> - <span class='Function'>|</span> <span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='String'>"β©"</span> <span class='Value'>valExpr</span> <span class='Comment'># Modified assignment +<span class='Value'>lhs</span> <span class='Function'>=</span> <span class='Value'>lhsSub</span> <span class='Function'>|</span> <span class='Value'>lhsStr</span> +<span class='Value'>subExpr</span> <span class='Function'>=</span> <span class='Value'>arg</span> + <span class='Function'>|</span> <span class='Value'>lhs</span> <span class='Function'>ASGN</span> <span class='Value'>subExpr</span> + <span class='Function'>|</span> <span class='Value'>lhs</span> <span class='Function'>Derv</span> <span class='String'>"β©"</span> <span class='Value'>subExpr</span> <span class='Comment'># Modified assignment </span></pre> -<p>A header looks like a name for the thing being headed, or its application to inputs (possibly twice in the case of modifiers and compositions). As with assignment, it is restricted to a simple form with no extra parentheses. The full list syntax is allowed for arguments. As a special rule, a monadic function header specifically can omit the function when the argument is not just a name (as this would conflict with a value label). The following cases define only headers with arguments, which are assumed to be special cases; there can be any number of these. Headers without arguments can only refer to the general caseβnote that operands are not pattern matchedβso there can be at most two of these kinds of headers, indicating the monadic and dyadic cases.</p> -<pre><span class='Value'>headW</span> <span class='Function'>=</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='String'>"π¨"</span> -<span class='Value'>headX</span> <span class='Function'>=</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='String'>"π©"</span> +<p>A header looks like a name for the thing being headed, or its application to inputs (possibly twice in the case of modifiers). As with assignment, it is restricted to a simple form with no extra parentheses. The full list syntax is allowed for arguments. As a special rule, a monadic function header specifically can omit the function when the argument is not just a name (as this would conflict with a subject label). The following cases define only headers with arguments, which are assumed to be special cases; there can be any number of these. Headers without arguments can only refer to the general caseβnote that operands are not pattern matchedβso there can be at most two of these kinds of headers, indicating the monadic and dyadic cases.</p> +<pre><span class='Value'>headW</span> <span class='Function'>=</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='String'>"π¨"</span> +<span class='Value'>headX</span> <span class='Function'>=</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='String'>"π©"</span> <span class='Function'>HeadF</span> <span class='Function'>=</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='String'>"π"</span> <span class='Function'>|</span> <span class='String'>"π½"</span> <span class='Function'>HeadG</span> <span class='Function'>=</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='String'>"π"</span> <span class='Function'>|</span> <span class='String'>"πΎ"</span> -<span class='Function'>ModH1</span> <span class='Function'>=</span> <span class='Function'>HeadF</span> <span class='Paren'>(</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='String'>"_π£"</span> <span class='Paren'>)</span> -<span class='Function'>CmpH1</span> <span class='Function'>=</span> <span class='Function'>HeadF</span> <span class='Paren'>(</span> <span class='Composition'>_c_</span> <span class='Function'>|</span> <span class='String'>"_π£_"</span> <span class='Paren'>)</span> <span class='Function'>HeadG</span> +<span class='Function'>Mod1H1</span> <span class='Function'>=</span> <span class='Function'>HeadF</span> <span class='Paren'>(</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='String'>"_π£"</span> <span class='Paren'>)</span> +<span class='Function'>Mod2H1</span> <span class='Function'>=</span> <span class='Function'>HeadF</span> <span class='Paren'>(</span> <span class='Modifier2'>_c_</span> <span class='Function'>|</span> <span class='String'>"_π£_"</span> <span class='Paren'>)</span> <span class='Function'>HeadG</span> <span class='Function'>FuncHead</span> <span class='Function'>=</span> <span class='Value'>headW?</span> <span class='Paren'>(</span> <span class='Function'>F</span> <span class='Function'>|</span> <span class='String'>"π"</span> <span class='Paren'>)</span> <span class='Value'>headX</span> - <span class='Function'>|</span> <span class='Value'>vl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Value'>valExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Value'>brVal</span> <span class='Function'>|</span> <span class='Value'>list</span> <span class='Comment'># value, -</span> <span class='Function'>|</span> <span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span> <span class='Comment'># but not v -</span><span class='Modifier'>_modHead</span> <span class='Function'>=</span> <span class='Value'>headW?</span> <span class='Function'>ModH1</span> <span class='Value'>headX</span> -<span class='Composition'>_cmpHed_</span> <span class='Function'>=</span> <span class='Value'>headW?</span> <span class='Function'>CmpH1</span> <span class='Value'>headX</span> + <span class='Function'>|</span> <span class='Value'>sl</span> <span class='Function'>|</span> <span class='String'>"("</span> <span class='Value'>subExpr</span> <span class='String'>")"</span> <span class='Function'>|</span> <span class='Value'>brSub</span> <span class='Function'>|</span> <span class='Value'>list</span> <span class='Comment'># subject, +</span> <span class='Function'>|</span> <span class='Function'>ANY</span> <span class='Paren'>(</span> <span class='String'>"βΏ"</span> <span class='Function'>ANY</span> <span class='Paren'>)</span><span class='Function'>+</span> <span class='Comment'># but not s +</span><span class='Modifier'>_m1Head</span> <span class='Function'>=</span> <span class='Value'>headW?</span> <span class='Function'>Mod1H1</span> <span class='Value'>headX</span> +<span class='Modifier2'>_m2Head_</span> <span class='Function'>=</span> <span class='Value'>headW?</span> <span class='Function'>Mod2H1</span> <span class='Value'>headX</span> </pre> -<p>A braced block contains bodies, which are lists of statements, separated by semicolons and possibly preceded by headers, which are separated from the body with a colon. Multiple bodies allow different handling for various cases, which are pattern-matched by headers. For a value block there are no inputs, so there can only be one possible case and one body. Functions and operators allow any number of "matched" bodies, with headers that have arguments, followed by at most two "main" bodies with either no headers or headers without arguments. If there is one main body, it is ambivalent, but two main bodies refer to the monadic and dyadic cases.</p> +<p>A braced block contains bodies, which are lists of statements, separated by semicolons and possibly preceded by headers, which are separated from the body with a colon. Multiple bodies allow different handling for various cases, which are pattern-matched by headers. For an immediate block there are no inputs, so there can only be one possible case and one body. Functions and modifiers allow any number of "matched" bodies, with headers that have arguments, followed by at most two "main" bodies with either no headers or headers without arguments. If there is one main body, it is ambivalent, but two main bodies refer to the monadic and dyadic cases.</p> <pre><span class='Function'>BODY</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Function'>STMT</span> <span class='Separator'>β</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Function'>EXPR</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Function'>FCase</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Function'>FuncHead</span> <span class='String'>":"</span> <span class='Function'>BODY</span> -<span class='Modifier'>_mCase</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Modifier'>_modHead</span> <span class='String'>":"</span> <span class='Function'>BODY</span> -<span class='Composition'>_cCase_</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Composition'>_cmpHed_</span> <span class='String'>":"</span> <span class='Function'>BODY</span> -<span class='Function'>FMain</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Function'>F</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> -<span class='Modifier'>_mMain</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Function'>ModH1</span> <span class='Paren'>)</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> -<span class='Composition'>_cMain_</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Composition'>_c_</span> <span class='Function'>|</span> <span class='Function'>CmpH1</span> <span class='Paren'>)</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> -<span class='Value'>brVal</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Value'>v</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> <span class='String'>"}"</span> +<span class='Modifier'>_mCase</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Modifier'>_m1Head</span> <span class='String'>":"</span> <span class='Function'>BODY</span> +<span class='Modifier2'>_cCase_</span> <span class='Function'>=</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Modifier2'>_m2Head_</span> <span class='String'>":"</span> <span class='Function'>BODY</span> +<span class='Function'>FMain</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Function'>F</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> +<span class='Modifier'>_mMain</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Modifier'>_m</span> <span class='Function'>|</span> <span class='Function'>Mod1H1</span> <span class='Paren'>)</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> +<span class='Modifier2'>_cMain_</span> <span class='Function'>=</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Paren'>(</span> <span class='Modifier2'>_c_</span> <span class='Function'>|</span> <span class='Function'>Mod2H1</span> <span class='Paren'>)</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> +<span class='Value'>brSub</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Separator'>β</span><span class='Value'>?</span> <span class='Value'>s</span> <span class='String'>":"</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>BODY</span> <span class='String'>"}"</span> <span class='Function'>BrFunc</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Function'>FCase</span> <span class='String'>";"</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Paren'>(</span> <span class='Function'>FCase</span> <span class='Function'>|</span> <span class='Function'>FMain</span> <span class='Paren'>(</span> <span class='String'>";"</span> <span class='Function'>FMain</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Paren'>)</span> <span class='String'>"}"</span> -<span class='Modifier'>_brMod</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Modifier'>_mCase</span> <span class='String'>";"</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Paren'>(</span> <span class='Modifier'>_mCase</span> <span class='Function'>|</span> <span class='Modifier'>_mMain</span> <span class='Paren'>(</span> <span class='String'>";"</span> <span class='Modifier'>_mMain</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Paren'>)</span> <span class='String'>"}"</span> -<span class='Composition'>_brComp_</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Composition'>_cCase_</span> <span class='String'>";"</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Paren'>(</span> <span class='Composition'>_cCase_</span> <span class='Function'>|</span> <span class='Composition'>_cMan_</span> <span class='Paren'>(</span> <span class='String'>";"</span> <span class='Composition'>_cMan_</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Paren'>)</span> <span class='String'>"}"</span> +<span class='Modifier'>_brMod1</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Modifier'>_mCase</span> <span class='String'>";"</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Paren'>(</span> <span class='Modifier'>_mCase</span> <span class='Function'>|</span> <span class='Modifier'>_mMain</span> <span class='Paren'>(</span> <span class='String'>";"</span> <span class='Modifier'>_mMain</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Paren'>)</span> <span class='String'>"}"</span> +<span class='Modifier2'>_brMod2_</span> <span class='Function'>=</span> <span class='String'>"{"</span> <span class='Paren'>(</span> <span class='Modifier2'>_cCase_</span> <span class='String'>";"</span> <span class='Paren'>)</span><span class='Value'>*</span> <span class='Paren'>(</span> <span class='Modifier2'>_cCase_</span> <span class='Function'>|</span> <span class='Modifier2'>_cMan_</span> <span class='Paren'>(</span> <span class='String'>";"</span> <span class='Modifier2'>_cMan_</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Paren'>)</span> <span class='String'>"}"</span> </pre> -<p>Two additional rules apply to blocks, based on the special name associations in the table below. First, each block allows the special names in its column to be used as the given token types within <code><span class='Function'>BODY</span></code> terms (not headers). Except for the spaces labelled "None", each column is cumulative and a given entry also includes all the entries above it. Second, for <code><span class='Function'>BrFunc</span></code>, <code><span class='Modifier'>_brMod</span></code>, and <code><span class='Composition'>_brComp_</span></code> terms, if no header is given, then at least one <code><span class='Function'>BODY</span></code> term in it <em>must</em> contain one of the names on, and not above, the corresponding row. Otherwise the syntax would be ambiguous, since for example a simple <code><span class='String'>"{"</span> <span class='Function'>BODY</span> <span class='String'>"}"</span></code> sequence could have any type.</p> +<p>Two additional rules apply to blocks, based on the special name associations in the table below. First, each block allows the special names in its column to be used as the given token types within <code><span class='Function'>BODY</span></code> terms (not headers). Except for the spaces labelled "None", each column is cumulative and a given entry also includes all the entries above it. Second, for <code><span class='Function'>BrFunc</span></code>, <code><span class='Modifier'>_brMod1</span></code>, and <code><span class='Modifier2'>_brMod2_</span></code> terms, if no header is given, then at least one <code><span class='Function'>BODY</span></code> term in it <em>must</em> contain one of the names on, and not above, the corresponding row. Otherwise the syntax would be ambiguous, since for example a simple <code><span class='String'>"{"</span> <span class='Function'>BODY</span> <span class='String'>"}"</span></code> sequence could have any type.</p> <table> <thead> <tr> <th>Term</th> -<th><code><span class='Value'>v</span></code></th> +<th><code><span class='Value'>s</span></code></th> <th><code><span class='Function'>F</span></code></th> <th><code><span class='Modifier'>_m</span></code></th> -<th><code><span class='Composition'>_c_</span></code></th> +<th><code><span class='Modifier2'>_c_</span></code></th> <th>other</th> </tr> </thead> <tbody> <tr> -<td><code><span class='Value'>brVal</span></code>, <code><span class='Function'>PROGRAM</span></code></td> +<td><code><span class='Value'>brSub</span></code>, <code><span class='Function'>PROGRAM</span></code></td> <td>None</td> <td>None</td> <td>None</td> @@ -111,28 +111,28 @@ <td><code><span class='String'>";"</span></code></td> </tr> <tr> -<td><code><span class='Modifier'>_brMod</span></code></td> +<td><code><span class='Modifier'>_brMod1</span></code></td> <td><code><span class='Value'>ππ£</span></code></td> <td><code><span class='Function'>π½</span></code></td> -<td><code><span class='Composition'>_</span><span class='Value'>π£</span></code></td> +<td><code><span class='Modifier2'>_</span><span class='Value'>π£</span></code></td> <td></td> <td></td> </tr> <tr> -<td><code><span class='Composition'>_brComp_</span></code></td> +<td><code><span class='Modifier2'>_brMod2_</span></code></td> <td><code><span class='Value'>π</span></code></td> <td><code><span class='Function'>πΎ</span></code></td> <td>None</td> -<td><code><span class='Composition'>_</span><span class='Value'>π£</span><span class='Composition'>_</span></code></td> +<td><code><span class='Modifier2'>_</span><span class='Value'>π£</span><span class='Modifier2'>_</span></code></td> <td></td> </tr> </tbody> </table> -<p>The rules for special name can be expressed in BNF by making many copies of all expression rules above. For each "level", or row in the table, a new version of every rule should be made that allows that level but not higher ones, and another version should be made that requires exactly that level. The values themselves should be included in <code><span class='Value'>v</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, and <code><span class='Composition'>_c_</span></code> for these copies. Then the "allowed" rules are made simply by replacing the terms they contain (excluding <code><span class='Value'>brVal</span></code> and so on) with the same "allowed" versions, and "required" rules are constructed using both "allowed" and "required" rules. For every part of a production rule, an alternative should be created that requires the relevant name in that part while allowing it in the others. For example, <code><span class='Paren'>(</span> <span class='Value'>value</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>arg</span></code> would be transformed to</p> -<pre><span class='Value'>arg_req1</span> <span class='Function'>=</span> <span class='Value'>valExpr_req1</span> - <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>value_req1</span> <span class='Function'>|</span> <span class='Value'>nothing_req1</span> <span class='Paren'>)</span> <span class='Function'>Derv_allow1</span> <span class='Value'>arg_allow1</span> - <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>value_allow1</span> <span class='Function'>|</span> <span class='Value'>nothing_allow1</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv_req1</span> <span class='Value'>arg_allow1</span> - <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>value_allow1</span> <span class='Function'>|</span> <span class='Value'>nothing_allow1</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv_allow1</span> <span class='Value'>arg_req1</span> +<p>The rules for special name can be expressed in BNF by making many copies of all expression rules above. For each "level", or row in the table, a new version of every rule should be made that allows that level but not higher ones, and another version should be made that requires exactly that level. The values themselves should be included in <code><span class='Value'>s</span></code>, <code><span class='Function'>F</span></code>, <code><span class='Modifier'>_m</span></code>, and <code><span class='Modifier2'>_c_</span></code> for these copies. Then the "allowed" rules are made simply by replacing the terms they contain (excluding <code><span class='Value'>brSub</span></code> and so on) with the same "allowed" versions, and "required" rules are constructed using both "allowed" and "required" rules. For every part of a production rule, an alternative should be created that requires the relevant name in that part while allowing it in the others. For example, <code><span class='Paren'>(</span> <span class='Value'>subject</span> <span class='Function'>|</span> <span class='Value'>nothing</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv</span> <span class='Value'>arg</span></code> would be transformed to</p> +<pre><span class='Value'>arg_req1</span> <span class='Function'>=</span> <span class='Value'>subExpr_req1</span> + <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>subject_req1</span> <span class='Function'>|</span> <span class='Value'>nothing_req1</span> <span class='Paren'>)</span> <span class='Function'>Derv_allow1</span> <span class='Value'>arg_allow1</span> + <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>subject_allow1</span> <span class='Function'>|</span> <span class='Value'>nothing_allow1</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv_req1</span> <span class='Value'>arg_allow1</span> + <span class='Function'>|</span> <span class='Paren'>(</span> <span class='Value'>subject_allow1</span> <span class='Function'>|</span> <span class='Value'>nothing_allow1</span> <span class='Paren'>)</span><span class='Value'>?</span> <span class='Function'>Derv_allow1</span> <span class='Value'>arg_req1</span> </pre> <p>Quite tedious. The explosion of rules is partly due to the fact that the brace-typing rule falls into a weaker class of grammars than the other rules. Most of BQN is <a href="https://en.wikipedia.org/wiki/Deterministic_context-free_grammar">deterministic context-free</a> but brace-typing is not, only context-free. Fortunately brace typing does not introduce the parsing difficulties that can be present in a general context-free grammar, and it can easily be performed in linear time: after <a href="token.html">scanning</a> but before parsing, move through the source code maintaining a stack of the current top-level set of braces. Whenever a colon or special name is encountered, annotate that set of braces to indicate that it is present. When a closing brace is encountered and the top brace is popped off the stack, the type is needed if there was no colon, and can be found based on which names were present. One way to present this information to the parser is to replace the brace tokens with new tokens that indicate the type.</p> diff --git a/docs/spec/literal.html b/docs/spec/literal.html index c4914753..2d03bc68 100644 --- a/docs/spec/literal.html +++ b/docs/spec/literal.html @@ -1,5 +1,5 @@ <head><link href="../style.css" rel="stylesheet"/></head> -<p>A <em>literal</em> is a single <a href="token.html">token</a> that indicates a fixed character, number, or array. While literals indicate data of a value type, <a href="primitive.html">primitives</a> indicate data of a function type: function, modifier, or composition.</p> +<p>A <em>literal</em> is a single <a href="token.html">token</a> that indicates a fixed character, number, or array. While literals indicate values of a data type, <a href="primitive.html">primitives</a> indicate values of an operation type: function, 1-modifier, or 2-modifier.</p> <p>Two types of literal deal with text. As the source code is considered to be a sequence of unicode code points ("characters"), and these code points are also used for BQN's character <a href="types.html">data type</a>, the representation of a text literal is very similar to its value. In a text literal, the newline character is always represented using the ASCII line feed character, code point 10. A <em>character literal</em> is enclosed with single quotes <code>'</code> and its value is identical to the single character between them. A <em>string literal</em> is enclosed in double quotes <code>"</code>, and any double quotes between them must come in pairs, as a lone double quote marks the end of the literal. The value of a string literal is a rank-1 array whose elements are the characters in between the enclosing quotes, after replacing each pair of double quotes with only one such quote.</p> <p>The format of a <em>numeric literal</em> is more complicated. From the <a href="token.html">tokenization rules</a>, a numeric literal consists of a numeric character (one of <code><span class='Number'>Β―βΟ.0123456789</span></code>) followed by any number of numeric or alphabetic characters. Some numeric literals are <em>valid</em> and indicate a number, while others are invalid and cause an error. The grammar for valid numbers is given below in a <a href="https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form">BNF</a> variant. Only four alphabetic characters are allowed: "i", which separates the real and imaginary components of a complex number, "e", which functions as in scientific notation, and the uppercase versions of these letters.</p> <pre><span class='Value'>number</span> <span class='Function'>=</span> <span class='Value'>component</span> <span class='Paren'>(</span> <span class='Paren'>(</span> <span class='String'>"i"</span> <span class='Function'>|</span> <span class='String'>"I"</span> <span class='Paren'>)</span> <span class='Value'>component</span> <span class='Paren'>)</span><span class='Value'>?</span> diff --git a/docs/spec/token.html b/docs/spec/token.html index 531cfd6e..f1408172 100644 --- a/docs/spec/token.html +++ b/docs/spec/token.html @@ -3,7 +3,7 @@ <p>BQN source code should be considered as a series of unicode code points, which we refer to as "characters". The separator between lines in a file is considered to be a single character, newline, even though some operating systems such as Windows typically represent it with a two-character CRLF sequence. Implementers should note that not all languages treat unicode code points as atomic, as exposing the UTF-8 or UTF-16 representation instead is common. For a language such as JavaScript that uses UTF-16, the double-struck characters <code><span class='Value'>π¨</span><span class='Function'>π</span><span class='Value'>π©</span><span class='Function'>π</span><span class='Value'>π</span><span class='Function'>π½</span><span class='Value'>π</span><span class='Function'>πΎ</span></code> are represented as two 16-bit surrogate characters, but BQN treats them as a single unit.</p> <p>A BQN <em>character literal</em> consists of a single character between single quotes, such as <code><span class='String'>'a'</span></code>, and a <em>string literal</em> consists of any number of characters between double quotes, such as <code><span class='String'>""</span></code> or <code><span class='String'>"abc"</span></code>. Character and string literals take precedence with comments over other tokenization rules, so that <code><span class='Comment'>#</span></code> between quotes does not start a comment and whitespace between quotes is not removed, but a quote within a comment does not start a character literal. Almost any character can be included directly in a character or string literal without escaping. The only exception is the double quote character <code>"</code>, which must be written twice to include it in a string, as otherwise it would end the string instead. Character literals require no escaping at all, as the length is fixed. In particular, literals for the double and single quote characters are written <code><span class='String'>'''</span></code> and <code><span class='String'>'"'</span></code>, while length-1 strings containing these characters are <code><span class='String'>"'"</span></code> and <code><span class='String'>""""</span></code>.</p> <p>A comment consists of the hash character <code><span class='Comment'>#</span></code> and any following text until (not including) the next newline character. The initial <code><span class='Comment'>#</span></code> must not be part of a string literal started earlier. Comments are ignored entirely and do not form tokens.</p> -<p>Identifiers and numeric literals share the same token formation rule. These tokens are formed from the <em>numeric characters</em> <code><span class='Number'>Β―βΟ.0123456789</span></code> and <em>alphabetic characters</em> <code><span class='Modifier'>_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</span></code> and the oddball <code><span class='Value'>π£</span></code>. Any sequence of these characters adjacent to each other forms a single token, which is a <em>numeric literal</em> if it begins with a numeric character and an <em>identifier</em> if it begins with an alphabetic character. Numeric literals are also subject to <a href="literal.html">numeric literal rules</a>, which specify which numeric literals are valid and which numbers they represent. If the token contains <code><span class='Value'>π£</span></code> it must be either <code><span class='Value'>π£</span></code>, <code><span class='Composition'>_</span><span class='Value'>π£</span></code>, or <code><span class='Composition'>_</span><span class='Value'>π£</span><span class='Composition'>_</span></code> and is considered a special name (see below). As the value taken by this identifier can only be a modifier or composition, the uppercase character <code><span class='Value'>β</span></code> is not allowed.</p> +<p>Identifiers and numeric literals share the same token formation rule. These tokens are formed from the <em>numeric characters</em> <code><span class='Number'>Β―βΟ.0123456789</span></code> and <em>alphabetic characters</em> <code><span class='Modifier'>_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</span></code> and the oddball <code><span class='Value'>π£</span></code>. Any sequence of these characters adjacent to each other forms a single token, which is a <em>numeric literal</em> if it begins with a numeric character and an <em>identifier</em> if it begins with an alphabetic character. Numeric literals are also subject to <a href="literal.html">numeric literal rules</a>, which specify which numeric literals are valid and which numbers they represent. If the token contains <code><span class='Value'>π£</span></code> it must be either <code><span class='Value'>π£</span></code>, <code><span class='Modifier2'>_</span><span class='Value'>π£</span></code>, or <code><span class='Modifier2'>_</span><span class='Value'>π£</span><span class='Modifier2'>_</span></code> and is considered a special name (see below). As the value taken by this identifier can only be a modifier, the uppercase character <code><span class='Value'>β</span></code> is not allowed.</p> <p>Following this step, the whitespace characters space and tab are ignored, and do not form tokens. Only these whitespace characters, and the newline character, which does form a token, are allowed.</p> <p>Otherwise, a single character forms a token. Only the specified set of characters can be used; others result in an error. The classes of characters are given below.</p> <table> @@ -19,12 +19,12 @@ <td><code><span class='Function'>+-ΓΓ·ββββ|Β¬β§β¨<>β =β€β₯β‘β’β£β’β₯βΎβββββ½β/ββββββββ·β</span></code></td> </tr> <tr> -<td>Primitive Modifier</td> +<td>Primitive 1-Modifier</td> <td><code><span class='Modifier'>ΛΛΒ¨ββΌΒ΄`</span></code></td> </tr> <tr> -<td>Primitive Composition</td> -<td><code><span class='Composition'>βββΈββΎββΆβββ</span></code></td> +<td>Primitive 1-Modifier</td> +<td><code><span class='Modifier2'>βββΈββΎββΆβββ</span></code></td> </tr> <tr> <td>Special name</td> diff --git a/docs/spec/types.html b/docs/spec/types.html index 81ed35ee..f1d5ee2a 100644 --- a/docs/spec/types.html +++ b/docs/spec/types.html @@ -5,16 +5,16 @@ <li>Number</li> <li>Array</li> <li>Function</li> -<li>Modifier</li> -<li>Composition</li> +<li>1-Modifier</li> +<li>2-Modifier</li> </ul> -<p>Of these, the first three are considered <em>value types</em> and the remaining three <em>function types</em>. We first describe the much simpler function types; the remainder of this page will be dedicated to the value types. A member of any function type accepts some number of <em>inputs</em> and either returns a <em>result</em> or causes an error; inputs and the result are data of any type. When a function is given inputs (<em>called</em>), it may produce side effects before returning, such as manipulating variables and calling other functions within its scope, or performing I/O.</p> +<p>Of these, the first three are considered <em>data types</em> and the remaining three <em>operation types</em>. We first describe the operation types; the remainder of this page will be dedicated to the data types. A member of any operation type accepts some number of <em>inputs</em> and either returns a <em>result</em> or causes an error; inputs and the result are values of any type. When a function is given inputs (<em>called</em>), it may produce side effects before returning, such as manipulating variables and calling other functions within its scope, or performing I/O.</p> <ul> <li>A <em>function</em> takes one (monadic call) or two (dyadic call) <em>arguments</em>.</li> -<li>A <em>modifier</em> takes one <em>operand</em>.</li> -<li>A <em>composition</em> takes two <em>operands</em>.</li> +<li>A <em>1-modifier</em> takes one <em>operand</em>.</li> +<li>A <em>2-modifier</em> takes two <em>operands</em>.</li> </ul> -<p>To begin the value types, a <em>character</em> is a <a href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> code point, that is, its value is a non-negative integer within the ranges defined by Unicode (however, it is distinct from this number as a BQN value). Characters are ordered by this numeric value. BQN deals with code points as abstract entities and does not use encodings such as UTF-8 or UTF-16.</p> +<p>To begin the data types, a <em>character</em> is a <a href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> code point, that is, its value is a non-negative integer within the ranges defined by Unicode (however, it is distinct from this number as a BQN value). Characters are ordered by this numeric value. BQN deals with code points as abstract entities and does not expose encodings such as UTF-8 or UTF-16.</p> <p>The precise type of a <em>number</em> may vary across BQN implementations or instances. A <em>real number</em> is a member of some supported subset of the <a href="https://en.wikipedia.org/wiki/Extended_real_number_line">extended real numbers</a>, that is, the real numbers and positive or negative infinity. Some system must be defined for rounding an arbitrary real number to a member of this subset, and the basic arithmetic operations add, subtract, multiply, divide, and natural exponent (base <em>e</em>) are defined by performing these operations on exact real values and rounding the result. The Power function (dyadic <code><span class='Function'>β</span></code>) is also used but need not be exactly rounded. A <em>complex number</em> is a value with two real number <em>components</em>, a <em>real part</em> and an <em>imaginary part</em>. A BQN implementation can either support real numbers only, or complex numbers.</p> <p>An <em>array</em> is a rectangular collection of data. It is defined by a <em>shape</em>, which is a list of non-negative integer lengths, and a <em>ravel</em>, which is a list of <em>elements</em> whose length (the array's <em>bound</em>) is the product of all lengths in the shape. Arrays are defined inductively: any value (of a value or function type) can be used as an element of an array, but it is not possible for an array to contain itself as an element, or an array that contains itself, and so on.</p> diff --git a/docs/style.css b/docs/style.css index b63bf5b9..a70821e5 100644 --- a/docs/style.css +++ b/docs/style.css @@ -45,7 +45,7 @@ pre { .Value { color: #1f2020; } .Function { color: #1f7229; } .Modifier { color: #7b3b60; } -.Composition { color: #857614; } +.Modifier2 { color: #857614; } .Gets { color: #16170a; } .Paren { color: #585f5b; } .Ligature, @@ -70,7 +70,7 @@ a:visited { color: #3d155f; } .Value { color: #a2acad; } .Function { color: #3aa548; } .Modifier { color: #92428a; } - .Composition{ color: #998819; } + .Modifier2 { color: #998819; } .Gets { color: #a5a896; } .Paren { color: #585047; } .Ligature, diff --git a/problems.md b/problems.md index c5ed3246..a6fe9d09 100644 --- a/problems.md +++ b/problems.md @@ -8,7 +8,7 @@ I've omitted problems that are obviously addressed by speculated extensions. Of A pretty fundamental problem with dynamically-typed array languages. Prototypes are intended to solve it, but they don't really. It doesn't help that the notion of type is fluid: elements of an array in one moment can be axis lengths in the next; did the numeric value go from not being type information to being type information? Inferred type might help here, particularly the ability of one part of the program to ask another part for type information during compilation. But that needs to be specified if programmers are going to rely on it, which sounds difficult. ### Control flow with function selection has awkward syntax -At the moment BQN has no control structures, instead preferring function recursion, iteration, and selection. Selection is awkward because the result of selecting from a list of functions must be a value syntactically. It also often doesn't need an argument, since it can refer to values from the containing function because of lexical scoping. There should probably be a function that takes a function argument and invokes it, possible with an empty list as a dummy left argument. Somewhat like the operator `{π½}`. But also: should a `{}` object that doesn't refer to its arguments be special? Some kind of "block" construct? +At the moment BQN has no control structures, instead preferring function recursion, iteration, and selection. Selection is awkward because the result of selecting from a list of functions is a subject syntactically. It also often doesn't need an argument, since it can refer to values from the containing function because of lexical scoping. There should possibly be a function that takes a function argument and invokes it, possible with an empty list as a dummy left argument. Somewhat like the modifier `{π½}`. *Potentially solved by multiple headers, blocks, and block returns. Needs reevaluation later.* @@ -22,7 +22,7 @@ There's been a lot of work done on this. Still there, still a problem. On the ot This problem hasn't manifested yet as BQN has no debugger, but it's something to keep in mind. Traditional line-by-line debuggers don't work when the line is doing so much work. Something like J's dissect or some kind of hybrid would probably do better. ### Search function depth -The simplest way to define a search function like Index Of is to require the left argument to be a list, and search for an element that matches the right argument. But this means you can only search for one element at a time, which is annoying and doesn't work for Progressive Index Of. So we instead treat the searched argument as a list of major cells. Then we decide to search for cells of the other argument that have the same rank as those cells, since only cells with the same rank can match. That's a little strange for Bins, where it still makes sense to compare cells of different ranks. Furthermore, the result of any search function is always an array. To search for a single element and get an unboxed number, you need something like `listβΈββΎ<elt`. +The simplest way to define a search function like Index Of is to require the left argument to be a list, and search for an element that matches the right argument. But this means you can only search for one element at a time, which is annoying and doesn't work for Progressive Index Of. So we instead treat the searched argument as a list of major cells. Then we decide to search for cells of the other argument that have the same rank as those cells, since only cells with the same rank can match. That's a little strange for Bins, where it still makes sense to compare cells of different ranks. Furthermore, the result of any search function is always an array. To search for a single element and get an plain number, you need something like `listβΈββΎ<elt`. ### Trigonometry There are a lot of standard functions and I don't want to use separate primitives or a menu-style primitive like APL Circle for them. You can define all the functions eventually if you use complex exponential and take real and imaginary parts and inverses, but this doesn't sound well-suited for implementation. And there should be a math library that gives you the standard functions with normal names, but how will it be implemented? @@ -42,10 +42,10 @@ It seems that the first is the most common, but the others aren't really rare. T The left argument feels much more like the primary one in these cases (indeed, this matches the typical left-to-right ordering of binary operators in mathematics). Not really fixable; too much precedent. ### Can't access array ordering directly -Only `ββ` use array ordering rather than just array comparison or numeric ordering. Getting at the actual ordering to just compare two arrays is more difficult than it should be (but not *that* difficult: `β₯βΈββΎ<` is TAO `β€`). +Only `ββ` use array ordering rather than just array equality or numeric ordering. Getting at the actual ordering to just compare two arrays is more difficult than it should be (but not *that* difficult: `β₯βΈββΎ<` is TAO `β€`). ### Syntactic type erasure -A programmer can call an operator on either a syntactic function or value, but there's no way to know within the operator which syntax that operand had. Maybe this is a better design, but it doesn't feel quite right that `fΛ` is `f`-Swap if `f` has a function value. The array syntax suggest it should be Constant. +A programmer can call a modifier on either a syntactic function or subject, but there's no way to know within the modifier which syntax that operand had. Maybe this is a better design, but it doesn't feel quite right that `fΛ` is `f`-Swap if `f` has a function value. The subject syntax suggests it should be Constant. ### Comparison tolerance Kind of necessary for practical programming, but how should it be invoked or controlled? A system variable like `βCT`? Per-primitive control? Both? Which primitives should use it? @@ -80,8 +80,8 @@ Both pull out elements and reduce the depth. But they face in opposite direction The directions of `ββ` and so on were mainly chosen to line up with `β`: the argument that indices apply to (that is, the one that is searched or selected from) corresponds to the open side of the function. I'd probably prefer new glyphs that don't have this sort of directionality, however. -### Converting a function expression to a syntactic value is tricky -You can name it, you can write `ββ¨Exprβ©`, and if it doesn't use special names you can write `{Expr}`. All of these are at least a little awkward in reasonable cases. Should there be a dedicated syntax? Note that going the other way, from value to function, isn't too bad: the operator `{π½}` does it. +### Converting a function expression to a subject is tricky +You can name it, you can write `ββ¨Exprβ©`, and if it doesn't use special names you can write `{Expr}`. All of these are at least a little awkward in reasonable cases. Should there be a dedicated syntax? Note that going the other way, from subject to function, isn't too bad: the modifier `{π½}` does it. ### Monadic argument corresponds to left for `/` and `β` Called dyadically, both functions shuffle cells of the right argument around, which is consistent with other selection-type functions. But the monadic case applies to what would be the left argument in the dyadic case. @@ -104,7 +104,7 @@ Boolean And (`β§`) and Or (`β¨`) are identical to Min (`β`) and Max (`β`) The other drawback of Min (`β§`) and Max (`β¨`) is that the symbols are counterintuitive, but I have found a way to remember them: consider the graph of variables `aβx` and `bβΒ¬x` for x from 0 to 1: two crossed lines. Now the graph of `aβ§b` is a caret shape and `aβ¨b` is a vee. ### Acting on windows can be awkward -When taking Windows along more than one axis, acting on the resulting array requires the Rank operator, duplicating either the right argument rank or (negated) left argument length. A nested Windows would only require Each. +When taking Windows along more than one axis, acting on the resulting array requires the Rank modifier, duplicating either the right argument rank or (negated) left argument length. A nested Windows would only require Each. ### Group doesn't include trailing empty groups But there are workarounds, described in [its documentation](doc/group.md). dzaima has suggested allowing a single extra element in the index argument to specify the result shape. Another possibility is for the result prototype to be specified to allow overtaking. @@ -124,9 +124,6 @@ Select chooses whether the left argument maps to right argument axes or selects ### Unclear primitive names Blanket issue for names that I don't find informative: "Solo", "Bins", "Unique Mask", "Find", and "Group". -### "Modifier" and "composition" terminology -Since the two have different syntax, it's important that they have completely separate names. "Modifier" is good but "composition" is not, as it doesn't seem to fit with Rank, Depth, or Iterate. There's also often a need for a term that refers to both. I've been using "operator" but that can be confusing. - ### Should have a rounding function There is a standard way to round floatsβto nearest integer, ties to evenβbut it's fairly hard to implement and would have to be specially recognized for performance. It would be nice to have a better way to access this. @@ -137,6 +134,9 @@ I went with "Index of" and "Less Than or Equal to" but the last word blends into Problems that existed in mainstream APL or a transitional BQN that have in my opinion been put to rest (while in some cases introducing new problems). Listed in reverse chronological order by time solved, by my recollection. +### "Modifier" and "composition" terminology +1-modifiers and 2-modifiers used to be called "modifiers" and "compositions", respectively, and sometimes "operators" collectively. The new names are much better, although they do leave a disconnect between the names for modifiers, and those for their inputsβ"operands". + ### Can't return from inner functions Fixed by adding block returns such as `labelβ` to jump out of a block with header name `label`. Hopefully these don't cause too many new problems. diff --git a/spec/evaluate.md b/spec/evaluate.md index 6442a499..a555740f 100644 --- a/spec/evaluate.md +++ b/spec/evaluate.md @@ -8,9 +8,9 @@ The result of parsing a valid BQN program is a `PROGRAM`, and the program is run A `PROGRAM` or `BODY` is a list of `STMT`s (for `BODY`, the last must be an `EXPR`, a particular kind of `STMT`), which are evaluated in program order. The statement `nothing` does nothing when evaluated, while `EXPR` evaluates some APL code and possibly assigns the results, as described below. -A block consists of several `BODY` terms, some of which may have an accompanying header describing accepted inputs and how they are processed. A value block `brVal` can only have one `BODY`, and is evaluated by evaluating the code in it. Other types of blocks do not evaluate any `BODY` immediately, but instead return a function, modifier, or operator that obtains its result by evaluating a particular `BODY`. The `BODY` is identified and evaluated once the block has received enough inputs (operands or arguments), which for modifiers and compositions can take one or two calls: if two calls are required, then on the first call the operands are simply stored and no code is evaluated yet. Two calls are required if there is more than one `BODY` term, if the `BODY` contains the special names `π¨π©π€πππ`, or if its header specifies arguments (the header-body is a `_mCase` or `_cCase_`). Otherwise only one is required. +A block consists of several `BODY` terms, some of which may have an accompanying header describing accepted inputs and how they are processed. An immediate block `brImm` can only have one `BODY`, and is evaluated by evaluating the code in it. Other types of blocks do not evaluate any `BODY` immediately, but instead return a function or modifier that obtains its result by evaluating a particular `BODY`. The `BODY` is identified and evaluated once the block has received enough inputs (operands or arguments), which for modifiers can take one or two calls: if two calls are required, then on the first call the operands are simply stored and no code is evaluated yet. Two calls are required if there is more than one `BODY` term, if the `BODY` contains the special names `π¨π©π€πππ`, or if its header specifies arguments (the header-body combination is a `_mCase` or `_cCase_`). Otherwise only one is required. -To evaluate a block when enough inputs have been received, first the correct case must be identified. To do this, first each special case (`FCase`, `_mCase`, or `_cCase_`) is checked in order to see if its arguments are strucurally compatible with the given arguments. That is, is `headW` is a `value`, there must be a left argument matching that structure, and if `headX` is a `value`, the right argument must match that structure. This means that `π¨` not only matches any left argument but also no argument. The test for compatibility is the same as for multiple assignment described below, except that the header may contain constants, which must match the corresponding part of the given argument.If no special case matches, then an appropriate general case (`FMain`, `_mMain`, or `_cMain_`) is used: if there are two, the first is used with no left argument and the second with a left argument; if there are one, it is always used, and if there are none, an error results. +To evaluate a block when enough inputs have been received, first the correct case must be identified. To do this, first each special case (`FCase`, `_mCase`, or `_cCase_`) is checked in order to see if its arguments are strucurally compatible with the given arguments. That is, is `headW` is a `subject`, there must be a left argument matching that structure, and if `headX` is a `subject`, the right argument must match that structure. This means that `π¨` not only matches any left argument but also no argument. The test for compatibility is the same as for multiple assignment described below, except that the header may contain constants, which must match the corresponding part of the given argument.If no special case matches, then an appropriate general case (`FMain`, `_mMain`, or `_cMain_`) is used: if there are two, the first is used with no left argument and the second with a left argument; if there are one, it is always used, and if there are none, an error results. The only remaining step before evaluating the `BODY` is to bind the inputs and other names. Special names are always bound when applicable: `π¨π©π€` if arguments are used, `π¨` if there is a left argument, `ππ` if operands are used, and `_π£` and `_π£_` for modifiers and combinators, respectively. Any names in the header are also bound, allowing multiple assignment for arguments. @@ -18,29 +18,29 @@ If there is no left argument, but the `BODY` contains `π¨` at the top level, t ### Assignment -An *assignment* is one of the four rules containing `ASGN`. It is evaluated by first evaluating the right-hand-side `valExpr`, `FuncExpr`, `_modExpr`, or `_cmpExp_` expression, and then storing the result in the left-hand-side identifier or identifiers. The result of the assignment expression is the result of its right-hand side. Except for values, only a lone identifier is allowed on the left-hand side and storage is obvious. For values, *multiple assignment* with a list left-hand side is also allowed. Multiple assignment is performed recursively by assigning right-hand-side values to the left-hand-side targets, with single-identifier (`v`) assignment as the base case. When matching the right-hand side to a list left-hand side, the left hand side is treated as a list of `lhs` targets. The evaluated right-hand side must be a list (rank-1 array) of the same length, and is matched to these targets element-wise. +An *assignment* is one of the four rules containing `ASGN`. It is evaluated by first evaluating the right-hand-side `subExpr`, `FuncExpr`, `_m1Expr`, or `_m2Exp_` expression, and then storing the result in the left-hand-side identifier or identifiers. The result of the assignment expression is the result of its right-hand side. Except for subjects, only a lone identifier is allowed on the left-hand side and storage is obvious. For subjects, *multiple assignment* with a list left-hand side is also allowed. Multiple assignment is performed recursively by assigning right-hand-side values to the left-hand-side targets, with single-identifier (`s`) assignment as the base case. When matching the right-hand side to a list left-hand side, the left hand side is treated as a list of `lhs` targets. The evaluated right-hand side must be a list (rank-1 array) of the same length, and is matched to these targets element-wise. -*Modified assignment* is the value assignment rule `lhs Derv "β©" valExpr`. In this case, `lhs` should be evaluated as if it were a `valExpr` (the syntax is a subset of `valExpr`), and the result of the function application `lhs Derv valExpr` should be assigned to `lhs`, and is also the result of the modified assignment expression. +*Modified assignment* is the subject assignment rule `lhs Derv "β©" subExpr`. In this case, `lhs` should be evaluated as if it were a `subExpr` (the syntax is a subset of `subExpr`), and the result of the function application `lhs Derv subExpr` should be assigned to `lhs`, and is also the result of the modified assignment expression. ### Expressions -We now give rules for evaluating an `atom`, `Func`, `_mod` or `_comp_` expression (the possible options for `ANY`). A literal `vl`, `Fl`, `_ml`, or `_cl_` has a fixed value defined by the specification ([value literals](literal.md) and [built-ins](primitive.md)). An identifier `v`, `F`, `_m`, or `_c_` is evaluated by returning its value; because of the scoping rules it must have one when evaluated. A parenthesized expression such as `"(" _modExpr ")"` simply returns the result of the interior expression. A braced construct such as `BraceFunc` is defined by the evaluation of the statements it contains after all parameters are accepted. Finally, a list `"β¨" β? ( ( EXPR β )* EXPR β? )? "β©"` or `ANY ( "βΏ" ANY )+` consists grammatically of a list of expressions. To evaluate it, each expression is evaluated in source order and their results are placed as elements of a rank-1 array. The two forms have identical semantics but different punctuation. +We now give rules for evaluating an `atom`, `Func`, `_mod1` or `_mod2_` expression (the possible options for `ANY`). A literal or primitive `sl`, `Fl`, `_ml`, or `_cl_` has a fixed value defined by the specification ([literals](literal.md) and [built-ins](primitive.md)). An identifier `s`, `F`, `_m`, or `_c_` is evaluated by returning its value; because of the scoping rules it must have one when evaluated. A parenthesized expression such as `"(" _modExpr ")"` simply returns the result of the interior expression. A braced construct such as `BraceFunc` is defined by the evaluation of the statements it contains after all parameters are accepted. Finally, a list `"β¨" β? ( ( EXPR β )* EXPR β? )? "β©"` or `ANY ( "βΏ" ANY )+` consists grammatically of a list of expressions. To evaluate it, each expression is evaluated in source order and their results are placed as elements of a rank-1 array. The two forms have identical semantics but different punctuation. -Rules in the table below are function and operator evaluation. -| L | Left | Called | Right | R | Types -|-----|-------------------------|----------|---------------------|-----|----------- -| `π¨` | `( value \| nothing )?` | `Derv` | `arg` | `π©` | Function, value -| `π` | `Operand` | `_mod` | | | Modifier -| `π` | `Operand` | `_comp_` | `( value \| Func )` | `π` | Composition +Rules in the table below are function and modifier evaluation. +| L | Left | Called | Right | R | Types +|-----|---------------------------|----------|-----------------------|-----|----------- +| `π¨` | `( subject \| nothing )?` | `Derv` | `arg` | `π©` | Function, subject +| `π` | `Operand` | `_mod1` | | | 1-Modifier +| `π` | `Operand` | `_mod2_` | `( subject \| Func )` | `π` | 2-Modifier -In each case the constituent expressions are evaluated in reverse source order: Right, then Called, then Left. Then the expression's result is obtained by calling the Called value on its parameters. A left argument of `nothing` is not used as a parameter, leaving only a right argument in that case. The data type of the Called value must be appropriate to the expression type, as indicated in the "Types" column. For function application, a value type (number, character, or array) is allowed. It is called simply by returning itself. Although the arguments are ignored in this case, they are still evaluated. A braced construct is evaluated by binding the parameter names given in columns L and R to the corresponding values. Then if all parameter levels present have been bound, its body is evaluated to give the result of application. +In each case the constituent expressions are evaluated in reverse source order: Right, then Called, then Left. Then the expression's result is obtained by calling the Called value on its parameters. A left argument of `nothing` is not used as a parameter, leaving only a right argument in that case. The type of the Called value must be appropriate to the expression type, as indicated in the "Types" column. For function application, a data type (number, character, or array) is allowed. It is called simply by returning itself. Although the arguments are ignored in this case, they are still evaluated. A braced construct is evaluated by binding the parameter names given in columns L and R to the corresponding values. Then if all parameter levels present have been bound, its body is evaluated to give the result of application. -The following rules derive new functions or operators from existing ones. -| Left | Center | Right | Result -|------------|-----------|---------------------|-------------- -| | `_comp_` | `( value \| Func )` | `{π½ _C_ R}` -| `Operand` | `_comp_` | | `{L _C_ π½}` -| `Operand` | `Derv` | `Fork` | `{(π¨Lπ©)C(π¨Rπ©)}` -| `nothing?` | `Derv` | `Fork` | `{ C(π¨Rπ©)}` +The following rules derive new functions or modifiers from existing ones. +| Left | Center | Right | Result +|------------|-----------|-----------------------|-------------- +| | `_mod2_` | `( subject \| Func )` | `{π½ _C_ R}` +| `Operand` | `_mod2_` | | `{L _C_ π½}` +| `Operand` | `Derv` | `Fork` | `{(π¨Lπ©)C(π¨Rπ©)}` +| `nothing?` | `Derv` | `Fork` | `{ C(π¨Rπ©)}` -As with applications, all expressions are evaluated in reverse source order before doing anything else. Then a result is formed without calling the center value. Its value in BQN is given in the rightmost column, using `L`, `C`, and `R` for the results of the expressions in the left, center, and right columns, respectively. For the first two rules (*partial application*), the given operand is bound to the composition: the result is a modifier that, when called, calls the center composition with the bound operand on the same side it appeared on and the new operand on the remaining side. A *train* is a function that, when called, calls the right-hand function on all arguments, then the left-hand function, and calls the center function with these results as arguments. In a composition partial application, the result will fail when applied if the center value does not have the composition type, and in a fork, it will fail if any component has a modifier or composition type (that is, cannot be applied as a function). BQN implementations are not required to check for these types when forming the result of these expressions, but may give an error on formation even if the result will never be applied. +As with applications, all expressions are evaluated in reverse source order before doing anything else. Then a result is formed without calling the center value. Its value in BQN is given in the rightmost column, using `L`, `C`, and `R` for the results of the expressions in the left, center, and right columns, respectively. For the first two rules (*partial application*), the given operand is bound to the 2-modifier: the result is a 1-modifier that, when called, calls the center 2-modifier with the bound operand on the same side it appeared on and the new operand on the remaining side. A *train* is a function that, when called, calls the right-hand function on all arguments, then the left-hand function, and calls the center function with these results as arguments. In a modifier partial application, the result will fail when applied if the center value does not have the 2-modifier type, and in a fork, it will fail if any component has a modifier type (that is, cannot be applied as a function). BQN implementations are not required to check for these types when forming the result of these expressions, but may give an error on formation even if the result will never be applied. diff --git a/spec/grammar.md b/spec/grammar.md index fe20e103..58544ce8 100644 --- a/spec/grammar.md +++ b/spec/grammar.md @@ -1,40 +1,40 @@ BQN's grammar is given below. Terms are defined in a [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) variant. However, handling special names properly is possible but difficult in BNF, so they are explained in text along with the braced block grammar. -The symbols `v`, `F`, `_m`, and `_c_` are identifier tokens with value, function, modifier, and composition classes respectively. Similarly, `vl`, `Fl`, `_ml`, and `_cl_` refer to value literals (numeric and character literals, or primitives) of those classes. While names in the BNF here follow the identifier naming scheme, this is informative only: syntactic classes are no longer used after parsing and cannot be inspected in a running program. +The symbols `s`, `F`, `_m`, and `_c_` are identifier tokens with subject, function, 1-modifier, and 2-modifier classes respectively. Similarly, `sl`, `Fl`, `_ml`, and `_cl_` refer to literals and primitives of those classes. While names in the BNF here follow the identifier naming scheme, this is informative only: syntactic classes are no longer used after parsing and cannot be inspected in a running program. A program is a list of statements. Almost all statements are expressions. However, explicit definitions and valueless results stemming from `Β·`, or `π¨` in a monadic brace function, can be used as statements but not expressions. PROGRAM = β? ( ( STMT β )* STMT β? )? STMT = EXPR | DEF | nothing β = ( "β" | "," | \n )+ - EXPR = valExpr | FuncExpr | _modExpr | _cmpExp_ + EXPR = subExpr | FuncExpr | _m1Expr | _m2Expr_ -Here we define the "atomic" forms of functions and operators, which are either single tokens or enclosed in paired symbols. Stranded vectors with `βΏ`, which binds more tightly than any form of execution, are also included. +Here we define the "atomic" forms of functions and modifiers, which are either single tokens or enclosed in paired symbols. Stranded vectors with `βΏ`, which binds more tightly than any form of execution, are also included. - ANY = atom | Func | _mod | _comp_ - _comp_ = _c_ | _cl_ | "(" _cmpExp_ ")" | _brComp_ - _mod = _m | _ml | "(" _modExpr ")" | _brMod - Func = F | Fl | "(" FuncExpr ")" | BrFunc - atom = v | vl | "(" valExpr ")" | brVal | list + ANY = atom | Func | _mod1 | _mod2_ + _mod2_ = _c_ | _cl_ | "(" _m1Expr_ ")" | _brMod2_ + _mod1 = _m | _ml | "(" _m2Expr ")" | _brMod1 + Func = F | Fl | "(" FuncExpr ")" | BrFunc + atom = s | sl | "(" subExpr ")" | brSub | list list = "β¨" β? ( ( EXPR β )* EXPR β? )? "β©" - value = atom | ANY ( "βΏ" ANY )+ + subject = atom | ANY ( "βΏ" ANY )+ -Starting at the highest-order objects, modifiers and compositions have fairly simple syntax. In most cases the syntax for `β` and `β©` is the same, but only `β©` can be used for modified assignment. +Starting at the highest-order objects, modifiers have fairly simple syntax. In most cases the syntax for `β` and `β©` is the same, but only `β©` can be used for modified assignment. ASGN = "β" | "β©" - _cmpExp_ = _comp_ - | _c_ ASGN _cmpExp_ - _modExpr = _mod - | _comp_ ( value | Func ) # Right partial application - | Operand _comp_ # Left partial application - | _m ASGN _modExpr + _m2Expr_ = _mod2_ + | _c_ ASGN _m2Expr_ + _m1Expr = _mod1 + | _mod2_ ( subject | Func ) # Right partial application + | Operand _mod2_ # Left partial application + | _m ASGN _m1Expr -Functions can be formed by fully applying operators or as trains. Operators are left-associative, so that the left operand (`Operand`) can include operators but the right operand (`value | Func`) cannot. Trains are right-associative, but bind less tightly than operators. Assignment is not allowed in the top level of a train: it must be parenthesized. +Functions can be formed by fully applying modifiers or as trains. modifiers are left-associative, so that the left operand (`Operand`) can include modifier applications but the right operand (`subject | Func`) cannot. Trains are right-associative, but bind less tightly than modifiers. Assignment is not allowed in the top level of a train: it must be parenthesized. Derv = Func - | Operand _mod - | Operand _comp_ ( value | Func ) - Operand = value + | Operand _mod1 + | Operand _mod2_ ( subject | Func ) + Operand = subject | Derv Fork = Derv | Operand Derv Fork # 3-train @@ -44,65 +44,65 @@ Functions can be formed by fully applying operators or as trains. Operators are FuncExpr = Train | F ASGN FuncExpr -Value expressions are complicated by the possibility of list assignment. We also define nothing-statements, which have very similar syntax to value expressions but do not permit assignment. +Subject expressions are complicated by the possibility of list assignment. We also define nothing-statements, which have very similar syntax to subject expressions but do not permit assignment. - arg = valExpr - | ( value | nothing )? Derv arg + arg = subExpr + | ( subject | nothing )? Derv arg nothing = "Β·" - | ( value | nothing )? Derv nothing - LHS_ANY = lhsValue | F | _m | _c_ + | ( subject | nothing )? Derv nothing + LHS_ANY = lhsSub | F | _m | _c_ LHS_ATOM = LHS_ANY | "(" lhsStr ")" LHS_ELT = LHS_ANY | lhsStr - lhsValue = v + lhsSub = s | "β¨" β? ( ( LHS_ELT β )* LHS_ELT β? )? "β©" lhsStr = LHS_ATOM ( "βΏ" LHS_ATOM )+ - lhs = lhsValue | lhsStr - valExpr = arg - | lhs ASGN valExpr - | lhs Derv "β©" valExpr # Modified assignment + lhs = lhsSub | lhsStr + subExpr = arg + | lhs ASGN subExpr + | lhs Derv "β©" subExpr # Modified assignment -A header looks like a name for the thing being headed, or its application to inputs (possibly twice in the case of modifiers and compositions). As with assignment, it is restricted to a simple form with no extra parentheses. The full list syntax is allowed for arguments. As a special rule, a monadic function header specifically can omit the function when the argument is not just a name (as this would conflict with a value label). The following cases define only headers with arguments, which are assumed to be special cases; there can be any number of these. Headers without arguments can only refer to the general caseβnote that operands are not pattern matchedβso there can be at most two of these kinds of headers, indicating the monadic and dyadic cases. +A header looks like a name for the thing being headed, or its application to inputs (possibly twice in the case of modifiers). As with assignment, it is restricted to a simple form with no extra parentheses. The full list syntax is allowed for arguments. As a special rule, a monadic function header specifically can omit the function when the argument is not just a name (as this would conflict with a subject label). The following cases define only headers with arguments, which are assumed to be special cases; there can be any number of these. Headers without arguments can only refer to the general caseβnote that operands are not pattern matchedβso there can be at most two of these kinds of headers, indicating the monadic and dyadic cases. - headW = value | "π¨" - headX = value | "π©" + headW = subject | "π¨" + headX = subject | "π©" HeadF = F | "π" | "π½" HeadG = F | "π" | "πΎ" - ModH1 = HeadF ( _m | "_π£" ) - CmpH1 = HeadF ( _c_ | "_π£_" ) HeadG + Mod1H1 = HeadF ( _m | "_π£" ) + Mod2H1 = HeadF ( _c_ | "_π£_" ) HeadG FuncHead = headW? ( F | "π" ) headX - | vl | "(" valExpr ")" | brVal | list # value, - | ANY ( "βΏ" ANY )+ # but not v - _modHead = headW? ModH1 headX - _cmpHed_ = headW? CmpH1 headX + | sl | "(" subExpr ")" | brSub | list # subject, + | ANY ( "βΏ" ANY )+ # but not s + _m1Head = headW? Mod1H1 headX + _m2Head_ = headW? Mod2H1 headX -A braced block contains bodies, which are lists of statements, separated by semicolons and possibly preceded by headers, which are separated from the body with a colon. Multiple bodies allow different handling for various cases, which are pattern-matched by headers. For a value block there are no inputs, so there can only be one possible case and one body. Functions and operators allow any number of "matched" bodies, with headers that have arguments, followed by at most two "main" bodies with either no headers or headers without arguments. If there is one main body, it is ambivalent, but two main bodies refer to the monadic and dyadic cases. +A braced block contains bodies, which are lists of statements, separated by semicolons and possibly preceded by headers, which are separated from the body with a colon. Multiple bodies allow different handling for various cases, which are pattern-matched by headers. For an immediate block there are no inputs, so there can only be one possible case and one body. Functions and modifiers allow any number of "matched" bodies, with headers that have arguments, followed by at most two "main" bodies with either no headers or headers without arguments. If there is one main body, it is ambivalent, but two main bodies refer to the monadic and dyadic cases. BODY = β? ( STMT β )* EXPR β? FCase = β? FuncHead ":" BODY - _mCase = β? _modHead ":" BODY - _cCase_ = β? _cmpHed_ ":" BODY - FMain = ( β? F ":" )? BODY - _mMain = ( β? ( _m | ModH1 ) ":" )? BODY - _cMain_ = ( β? ( _c_ | CmpH1 ) ":" )? BODY - brVal = "{" ( β? v ":" )? BODY "}" + _mCase = β? _m1Head ":" BODY + _cCase_ = β? _m2Head_ ":" BODY + FMain = ( β? F ":" )? BODY + _mMain = ( β? ( _m | Mod1H1 ) ":" )? BODY + _cMain_ = ( β? ( _c_ | Mod2H1 ) ":" )? BODY + brSub = "{" ( β? s ":" )? BODY "}" BrFunc = "{" ( FCase ";" )* ( FCase | FMain ( ";" FMain )? ) "}" - _brMod = "{" ( _mCase ";" )* ( _mCase | _mMain ( ";" _mMain )? ) "}" - _brComp_ = "{" ( _cCase_ ";" )* ( _cCase_ | _cMan_ ( ";" _cMan_ )? ) "}" + _brMod1 = "{" ( _mCase ";" )* ( _mCase | _mMain ( ";" _mMain )? ) "}" + _brMod2_ = "{" ( _cCase_ ";" )* ( _cCase_ | _cMan_ ( ";" _cMan_ )? ) "}" -Two additional rules apply to blocks, based on the special name associations in the table below. First, each block allows the special names in its column to be used as the given token types within `BODY` terms (not headers). Except for the spaces labelled "None", each column is cumulative and a given entry also includes all the entries above it. Second, for `BrFunc`, `_brMod`, and `_brComp_` terms, if no header is given, then at least one `BODY` term in it *must* contain one of the names on, and not above, the corresponding row. Otherwise the syntax would be ambiguous, since for example a simple `"{" BODY "}"` sequence could have any type. +Two additional rules apply to blocks, based on the special name associations in the table below. First, each block allows the special names in its column to be used as the given token types within `BODY` terms (not headers). Except for the spaces labelled "None", each column is cumulative and a given entry also includes all the entries above it. Second, for `BrFunc`, `_brMod1`, and `_brMod2_` terms, if no header is given, then at least one `BODY` term in it *must* contain one of the names on, and not above, the corresponding row. Otherwise the syntax would be ambiguous, since for example a simple `"{" BODY "}"` sequence could have any type. -| Term | `v` | `F` | `_m` | `_c_` | other +| Term | `s` | `F` | `_m` | `_c_` | other |--------------------|--------|--------|---------|----------|------- -| `brVal`, `PROGRAM` | None | None | None | None | +| `brSub`, `PROGRAM` | None | None | None | None | | `BrFunc` | `π¨π©π€` | `πππ` | | | `";"` -| `_brMod` | `ππ£` | `π½` | `_π£` | | -| `_brComp_` | `π` | `πΎ` | None | `_π£_` | +| `_brMod1` | `ππ£` | `π½` | `_π£` | | +| `_brMod2_` | `π` | `πΎ` | None | `_π£_` | -The rules for special name can be expressed in BNF by making many copies of all expression rules above. For each "level", or row in the table, a new version of every rule should be made that allows that level but not higher ones, and another version should be made that requires exactly that level. The values themselves should be included in `v`, `F`, `_m`, and `_c_` for these copies. Then the "allowed" rules are made simply by replacing the terms they contain (excluding `brVal` and so on) with the same "allowed" versions, and "required" rules are constructed using both "allowed" and "required" rules. For every part of a production rule, an alternative should be created that requires the relevant name in that part while allowing it in the others. For example, `( value | nothing )? Derv arg` would be transformed to +The rules for special name can be expressed in BNF by making many copies of all expression rules above. For each "level", or row in the table, a new version of every rule should be made that allows that level but not higher ones, and another version should be made that requires exactly that level. The values themselves should be included in `s`, `F`, `_m`, and `_c_` for these copies. Then the "allowed" rules are made simply by replacing the terms they contain (excluding `brSub` and so on) with the same "allowed" versions, and "required" rules are constructed using both "allowed" and "required" rules. For every part of a production rule, an alternative should be created that requires the relevant name in that part while allowing it in the others. For example, `( subject | nothing )? Derv arg` would be transformed to - arg_req1 = valExpr_req1 - | ( value_req1 | nothing_req1 ) Derv_allow1 arg_allow1 - | ( value_allow1 | nothing_allow1 )? Derv_req1 arg_allow1 - | ( value_allow1 | nothing_allow1 )? Derv_allow1 arg_req1 + arg_req1 = subExpr_req1 + | ( subject_req1 | nothing_req1 ) Derv_allow1 arg_allow1 + | ( subject_allow1 | nothing_allow1 )? Derv_req1 arg_allow1 + | ( subject_allow1 | nothing_allow1 )? Derv_allow1 arg_req1 Quite tedious. The explosion of rules is partly due to the fact that the brace-typing rule falls into a weaker class of grammars than the other rules. Most of BQN is [deterministic context-free](https://en.wikipedia.org/wiki/Deterministic_context-free_grammar) but brace-typing is not, only context-free. Fortunately brace typing does not introduce the parsing difficulties that can be present in a general context-free grammar, and it can easily be performed in linear time: after [scanning](token.md) but before parsing, move through the source code maintaining a stack of the current top-level set of braces. Whenever a colon or special name is encountered, annotate that set of braces to indicate that it is present. When a closing brace is encountered and the top brace is popped off the stack, the type is needed if there was no colon, and can be found based on which names were present. One way to present this information to the parser is to replace the brace tokens with new tokens that indicate the type. diff --git a/spec/literal.md b/spec/literal.md index 0f22909f..937363e0 100644 --- a/spec/literal.md +++ b/spec/literal.md @@ -1,4 +1,4 @@ -A *literal* is a single [token](token.md) that indicates a fixed character, number, or array. While literals indicate data of a value type, [primitives](primitive.md) indicate data of a function type: function, modifier, or composition. +A *literal* is a single [token](token.md) that indicates a fixed character, number, or array. While literals indicate values of a data type, [primitives](primitive.md) indicate values of an operation type: function, 1-modifier, or 2-modifier. Two types of literal deal with text. As the source code is considered to be a sequence of unicode code points ("characters"), and these code points are also used for BQN's character [data type](types.md), the representation of a text literal is very similar to its value. In a text literal, the newline character is always represented using the ASCII line feed character, code point 10. A *character literal* is enclosed with single quotes `'` and its value is identical to the single character between them. A *string literal* is enclosed in double quotes `"`, and any double quotes between them must come in pairs, as a lone double quote marks the end of the literal. The value of a string literal is a rank-1 array whose elements are the characters in between the enclosing quotes, after replacing each pair of double quotes with only one such quote. diff --git a/spec/reference.bqn b/spec/reference.bqn index 2072f96a..bba39aef 100644 --- a/spec/reference.bqn +++ b/spec/reference.bqn @@ -176,7 +176,7 @@ DepthβIsArrayβΆ0βΏ{1+0βΒ΄DepthΒ¨β₯π©} #β # LAYER 4: Operators -> β© Unbox β > +> β© Merge β > β β >βPair β β _rankOp_ β β _depthOp_ @@ -188,7 +188,7 @@ DropVβ {ββπ©Β¨π¨+βπ¨-Λβ π©} Cell β DropVββ’ Pair β {β¨π©β©} β {β¨π¨,π©β©} -Unboxβ(0<β ββ₯)βΆβ’βΏ{ +Mergeβ(0<β ββ₯)βΆβ’βΏ{ cββ’βπ© ! β§Β΄β₯(cβ‘β’)Β¨π© π©ββToArrayΛββc diff --git a/spec/token.md b/spec/token.md index 8315c9c2..3c2235eb 100644 --- a/spec/token.md +++ b/spec/token.md @@ -6,7 +6,7 @@ A BQN *character literal* consists of a single character between single quotes, A comment consists of the hash character `#` and any following text until (not including) the next newline character. The initial `#` must not be part of a string literal started earlier. Comments are ignored entirely and do not form tokens. -Identifiers and numeric literals share the same token formation rule. These tokens are formed from the *numeric characters* `Β―βΟ.0123456789` and *alphabetic characters* `_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` and the oddball `π£`. Any sequence of these characters adjacent to each other forms a single token, which is a *numeric literal* if it begins with a numeric character and an *identifier* if it begins with an alphabetic character. Numeric literals are also subject to [numeric literal rules](literal.md), which specify which numeric literals are valid and which numbers they represent. If the token contains `π£` it must be either `π£`, `_π£`, or `_π£_` and is considered a special name (see below). As the value taken by this identifier can only be a modifier or composition, the uppercase character `β` is not allowed. +Identifiers and numeric literals share the same token formation rule. These tokens are formed from the *numeric characters* `Β―βΟ.0123456789` and *alphabetic characters* `_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` and the oddball `π£`. Any sequence of these characters adjacent to each other forms a single token, which is a *numeric literal* if it begins with a numeric character and an *identifier* if it begins with an alphabetic character. Numeric literals are also subject to [numeric literal rules](literal.md), which specify which numeric literals are valid and which numbers they represent. If the token contains `π£` it must be either `π£`, `_π£`, or `_π£_` and is considered a special name (see below). As the value taken by this identifier can only be a modifier, the uppercase character `β` is not allowed. Following this step, the whitespace characters space and tab are ignored, and do not form tokens. Only these whitespace characters, and the newline character, which does form a token, are allowed. @@ -15,8 +15,8 @@ Otherwise, a single character forms a token. Only the specified set of character | Class | Characters |-----------------------|------------ | Primitive Function | `+-ΓΓ·ββββ\|Β¬β§β¨<>β =β€β₯β‘β’β£β’β₯βΎβββββ½β/ββββββββ·β` -| Primitive Modifier | `` ΛΛΒ¨ββΌΒ΄` `` -| Primitive Composition | `βββΈββΎββΆβββ` +| Primitive 1-Modifier | `` ΛΛΒ¨ββΌΒ΄` `` +| Primitive 1-Modifier | `βββΈββΎββΆβββ` | Special name | `π¨π©πππ€πππ½πΎπ` | Punctuation | `ββ©β(){}β¨β©βΏβ,` and newline diff --git a/spec/types.md b/spec/types.md index fa3f311e..0d142630 100644 --- a/spec/types.md +++ b/spec/types.md @@ -3,15 +3,15 @@ BQN programs manipulate data of six types: - Number - Array - Function -- Modifier -- Composition +- 1-Modifier +- 2-Modifier -Of these, the first three are considered *value types* and the remaining three *function types*. We first describe the much simpler function types; the remainder of this page will be dedicated to the value types. A member of any function type accepts some number of *inputs* and either returns a *result* or causes an error; inputs and the result are data of any type. When a function is given inputs (*called*), it may produce side effects before returning, such as manipulating variables and calling other functions within its scope, or performing I/O. +Of these, the first three are considered *data types* and the remaining three *operation types*. We first describe the operation types; the remainder of this page will be dedicated to the data types. A member of any operation type accepts some number of *inputs* and either returns a *result* or causes an error; inputs and the result are values of any type. When a function is given inputs (*called*), it may produce side effects before returning, such as manipulating variables and calling other functions within its scope, or performing I/O. - A *function* takes one (monadic call) or two (dyadic call) *arguments*. -- A *modifier* takes one *operand*. -- A *composition* takes two *operands*. +- A *1-modifier* takes one *operand*. +- A *2-modifier* takes two *operands*. -To begin the value types, a *character* is a [Unicode](https://en.wikipedia.org/wiki/Unicode) code point, that is, its value is a non-negative integer within the ranges defined by Unicode (however, it is distinct from this number as a BQN value). Characters are ordered by this numeric value. BQN deals with code points as abstract entities and does not use encodings such as UTF-8 or UTF-16. +To begin the data types, a *character* is a [Unicode](https://en.wikipedia.org/wiki/Unicode) code point, that is, its value is a non-negative integer within the ranges defined by Unicode (however, it is distinct from this number as a BQN value). Characters are ordered by this numeric value. BQN deals with code points as abstract entities and does not expose encodings such as UTF-8 or UTF-16. The precise type of a *number* may vary across BQN implementations or instances. A *real number* is a member of some supported subset of the [extended real numbers](https://en.wikipedia.org/wiki/Extended_real_number_line), that is, the real numbers and positive or negative infinity. Some system must be defined for rounding an arbitrary real number to a member of this subset, and the basic arithmetic operations add, subtract, multiply, divide, and natural exponent (base *e*) are defined by performing these operations on exact real values and rounding the result. The Power function (dyadic `β`) is also used but need not be exactly rounded. A *complex number* is a value with two real number *components*, a *real part* and an *imaginary part*. A BQN implementation can either support real numbers only, or complex numbers. |
