aboutsummaryrefslogtreecommitdiff
path: root/spec/evaluate.md
diff options
context:
space:
mode:
authorMarshall Lochbaum <mwlochbaum@gmail.com>2021-08-23 21:01:04 -0400
committerMarshall Lochbaum <mwlochbaum@gmail.com>2021-08-23 21:01:04 -0400
commit2a4cf47c8a72720d8ccddfa0e7a838ca60bfa6b5 (patch)
tree18648da9e95df754e2024bd5f4bd09f2458757ab /spec/evaluate.md
parent5930bc759887d866aba7e77c6943a0f3e6053454 (diff)
Define a derived operation in the spec
Diffstat (limited to 'spec/evaluate.md')
-rw-r--r--spec/evaluate.md8
1 files changed, 5 insertions, 3 deletions
diff --git a/spec/evaluate.md b/spec/evaluate.md
index 9ac91e53..12f55332 100644
--- a/spec/evaluate.md
+++ b/spec/evaluate.md
@@ -14,7 +14,7 @@ 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, which are evaluated in program order. A result is always required for `BODY` nodes, and sometimes for `PROGRAM` nodes (for example, when loaded with `β€’Import`). If any identifiers in the node's scope are exported, or any of its statements is an `EXPORT`, then the result is the namespace created in order to evaluate the node. If a result is required but the namespace case doesn't apply, then the last `STMT` node must be an `EXPR` and its result is used. The statement `EXPR` evaluates some APL code and possibly assigns the results, while `nothing` evaluates any `subject` or `Derv` terms it contains but discards the results. An `EXPORT` statement performs no action.
-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.
+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. The stored values can be accessed by equality checking, or `β€’Decompose` if defined. 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_`), excluding `FCase` nodes containing `UndoHead`, 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.
@@ -45,7 +45,9 @@ Rules in the table below are function and modifier evaluation.
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 modifiers from existing ones.
+Modifiers that are evaluated when they receive operands are called *immediate*. Other modifiers, including primitives and some kinds of block, simply record the operands and are called *deferred*. The result of applying a deferred modifier once is called a *derived function*.
+
+The following rules always create *derived operations*, either 1-modifiers or derived functions. A derived operation is identified by the rule that created it, and the values of its parts.
| Left | Center | Right | Result
|------------|-----------|-----------------------|--------------
| | `_mod2_` | `( subject \| Func )` | `{𝔽 _C_ R}`
@@ -53,4 +55,4 @@ The following rules derive new functions or modifiers from existing ones.
| `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 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.
+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 behavior as a function is described 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.