RTensor is the sixth-best formula calculator in the world.
(I wrote that slogan a long while ago.
Don't ask what the five better ones are.)
## Concepts
### AST
All valid code in the RTensor language gets parsed into an **abstract syntax tree** (**AST**).
The AST consists of nodes, which may or may not reference subnodes, making them "branch nodes" (as e.g. operations and function calls) and "leaf nodes" (as primitives) respectively.
ASTs are common to many languages, and you don't really need to know about them for basic RTensor use.
### Tensor
Consider the following progression:
- **scalar**: a single number (rank 0)
- **vector**: a list of numbers (rank 1)
- **matrix**: a list of lists of numbers (rank 2)
All items in that progression qualify as **tensors** of various **ranks**;
ranks of tensors continue beyond 2 as far as you want, tho ranks from 0 to 2 occur most commonly.
## Interface
The most developed [interface for RTensor is the webpage]( /rtensor/rtensor.xhtml ), which consists of some documentative content, followed by a space for alternating lines of input and output.
### Running code
To run a line of RTensor code, type it (or copy it) into the current input line and press `Enter`.
You can also use the up and down arrow keys to navigate thru the current session's history of earlier lines.
Whilst not in the RTensor language, you can combine multiple lines of code into one input by joining them with semicolons (`;`).
### Successful output
When no error occurs whilst processing the input code, RTensor will write as output the code's return value.
Preceding this return value, RTensor indicates a variable name (an underscore followed by a serial number) to which it saved that return value.
If the input code consists of multiple semicolon-separated statements, RTensor will evaluate all of the statements, but will only present the last one's return value, and the serial variable will only receive the final statement's return value.
RTensor also silently saves the final return value to the variable `_` (underscore), but leaves its previous value if an error occurred.
### Extra output
Some code (such as syntactic functions) produces extra output, in varying forms, before the main output.
### Error messages
When an error occurs whilst parsing or evaluating the input code, RTensor will display a relevant error message.
If the error starts with a `PascalCase` error type, that means it came directly from JavaScript, and should be regarded as a bug.
If the error occurs when evaluating, RTensor will not execute code after the part that triggered the error;
errors propagate up thru a program.
For some code (such as `3+`), RTensor successfully parses and evaluates it, but notices that not all of the code corresponds to part of a valid AST.
In addition to typical output, RTensor leaves the message "possible syntax error", which *almost always* means "actual syntax error".
## Primitives
### Numeric literals
A numeric literal directly represents a real number (a scalar), and consists of one or both of an integer component and a fractional component.
Each component consists of digits (in base ten).
The fractional component, if present, must begin with a dot (`.`).
Valid numeric literals include `0`, `389`, `3.14159`, and `.367`.
For now, you can use `.` to mean `NaN`, but this may change.
### Identifiers
An identifier consists of a sequence of letters, digits, underscores, or (unlike most other languages) apostrophes (akin to prime symbols), and does not start with a digit.
An identifier, when interpreted to evaluating code, references a variable, which can hold a value of any type.
Shadowing/function scope may affect what variable an identifier references.
## Operators
**Operators** combine one or two subexpressions (making the operation **unary** or **binary**, respectively) and describe a computation based on the subexpressions.
Unary operators go before their subexpression.
Binary operators go between their subexpressions.
Each operator has a **precedence**, which may group it more tightly or loosely than other operators, and each precedence has an **associativity**.
Unary operators all have tighter precedence than binary operators, regardless of their relative precedence as binary operators.
You can use parentheses to group operations differently from what their precedence and associativity would normally imply.
In operator descriptions, `a` and `b` represent subexpressions and their results.
### Basic operators
When used on a scalar and a tensor, the result comes from the tensor, with each element replaced by the result of the operation between the scalar and each scalar in the tensor.
For example, `3 * ((7, 2), (-1, 3), (-8, -2))` returns `[[21, 6], [-3, 9], [-24, -6]]`.
When used on two tensors, the result comes from the elementwise combination of the two tensors, or an error if the tensors have different structures.
- addition: `a + b`, or `+a` (identity)
- subtraction: `a - b`, or `-a` (negative)
- multiplication: `a * b`
- division: `a / b`, or `/a` (reciprocal)
- exponentiation: `a ^ b`, or `^a` (exp(`a`) = `e`^{a})
- logarithms: `a \ b` (base-`a` logarithm of `b`), or `\a` (natural logarithm)
- equality: `a == b` (true or false), or `== a` (equal to zero)
- radicals: `a -/ b` (`a`th root of `b`), or `-/a` (square root)
- comparison: `a < b` (true or false), or `< a` (negative)
You cannot chain comparisons, as in `a < b < c`;
RTensor will interpret that as `(a < b) < c`, which reduces to either `true < c` or `false < c`.
RTensor has no `>` operator.
Instead, swap operands as appropriate.
### Appending
`a, b` gives an output which depends on the relative rank of `a` and `b`:
- if `a` has greater rank, RTensor appends `b` to the end of `a`, returning a new tensor
- otherwise, RTensor makes a two-element tensor, consisting of `a` and `b` as its elements
These semantics let you chain the appending operator (as in `a, b, c, d`) to build a many-element tensor.
### Concatenating
`a .. b` gives an output which depends on the ranks of `a` and `b`:
- if both ranks exceed zero, RTensor concatenates `b` to the end of `a`, returning a new tensor
- if both ranks equal zero (`a` and `b` are scalars), RTensor generates a vector by iterating thru the numbers from `a` to `b` (inclusive) with a step size of 1
- otherwise, `a .. b` is an error
When using the concatenation operator, carefully distinguish it from a decimal point;
e.g. `3..5` will result in an error, unlike the valid options `3.5` and `3 .. 5`.
`.. a` encapsulates `a` as a one-element tensor, effectively incrementing the rank without adding any new data.
### Maplet
`a => b` constructs and returns an anonymous function.
`a` may consist of a comma-separated list, each of which labels a separate argument for the function;
it gets tranlated into a pattern, but syntactically forms as an expression.
`b` must consist of some expression, possibly using variables defined in `a`.
For example, `x, y => x + y` makes addition into a two-input function.
### Assignment
The assignment operation, `a = b`, saves the value from `b` into the location described by `a`, and returns the new value of `a` (or something closely related to `a`, by undocumented semantics).
`a` may consist of an identifier — in which case, `b` replaces the value already stored in that variable.
`a` may have a function call or index suffix (the two have the same form):
- if `a` already references a function, the assignment adds a new case (the pattern consisting of the argument list, the expression coming from `b`) to that function (at top priority)
- if `a` already references a tensor, the assignment replaces the element at the given index with the value of `b`, or causes an error if `a` does not already contain an element at that index
- if `a` does not reference anything, or references a scalar, the assignment replaces the value with a new function, adding a single case as if `a` already referenced a function
### Precedence ranking
From tightest to loosest, L = left-associative, R = right-associative:
1. logarithms, R
2. exponentiation, radicals, R
3. multiplication, division, L
4. addition, subtraction, concatenation, L
5. equality, comparison, L
6. appending, L
7. maplet, R
8. assignment, R
## Indexing
An expression suffixed with a parenthesised expression (`v(i)`), or the equivalent bracketed form (`v[i]`), forms an **indexing expression** (or a function call, depending on `v`);
if `v` refers to a tensor, the index expression returns the `i`th element of `v` (a tensor of rank at most one less than that of `v`).
Indexing in RTensor starts at 1 and not 0.
Attempts to access `v(i)` where `i` exceeds the length of `v`, `i` does not equal an integer, or `i` subceeds 1 will cause an error.
The `i` component of the indexing expression can include extra comma-separated indices, but RTensor currently ignores all indices after the first.
## Function calls
An expression suffixed with a parenthesised expression-list (`f(a, b, c, ...)`) forms a **function call** (or an indexing expression, depending on `f`);
if `f` refers to a function, the function call returns the value `f` provides when evaluated with the given argument list.
The argument list may contain any number of arguments, including 0 (i.e. `f()`).
Whilst `,` normally acts as an operator, the argument-list syntax suppresses this behaviour.
In an argument list, `,` acts as an operator iff it occurs in parentheses, as in the first argument of `f((a, b), c)`.
Operators with precedence looser than that of `,` (i.e. `=>` and `=`) will not work at all unless parenthesised.
### Evaluation process
When evaluating a function, RTensor first evaluates all the arguments, then searches for a pattern (going in descending priority) that matches the arguments.
If RTensor could not find a matching pattern, an error occurs.
Upon finding a matching pattern, RTensor evaluates the expression or internal function corresponding to that pattern in the function's definition, using the argument-name correspondences given from the pattern to make local variables for that expression.
### Syntactic function calls
If a function is called with bracket syntax (`f[a, b, c, ...]`) rather than the parenthetical syntax, the arguments will be given to the function as expressions (syntax), not values;
they will be left unevaluated.
Whether this is useful depends on the function.
Typically, a function is only usable with either evaluated arguments (the more common case) or syntactic arguments.
## Function definitions
**Functions**, as objects in RTensor (or **function definitions**), consist of a list (arranged by priority) of pattern-expression pairs, or **cases**.
Some functions (those predefined by the code which sets up RTensor) have, in place of an expression defined from RTensor, a function in the language implementing RTensor.
### Pattern matching
A **pattern** consists of a list of zero or more **parameters**.
A parameter consists of either an identifier or some other expression:
- in the former case, that parameter matches any value, and will require RTensor to create a local variable with that name to store that value, *even if a variable defined in a scope further out already exists with that name*
- in the latter case, RTensor evaluates the expression (at the time of case-creation), using only variables defined in a scope further out than the function evaluation;
that parameter matches only values considered equal to the result of the expression
When checking if a pattern matches a list of arguments, the arguments get aligned to the parameters;
if the number of arguments differs from the number of parameters, the pattern does not match without any further checks.
To make a parameter that matches only values which equal an existing variable, wrap the variable identifier in an identity (such as `x + 0` or `x * 1`).
RTensor currently does not provide pattern-matching mechanisms more complex than equality checks against constant values;
you must do so yourself with explicit checks.
### Anonymous functions
RTensor provides both an assignment-based syntax and an anonymous maplet-based syntax for defining functions.
The former works with (named) variables, but the latter constructs a function without saving it to a variable (anonymous).
A maplet operation takes its left operand as a pattern and its right operand as an unevaluated sub-expression, constructing and returning a single-case function, usable in the same contexts as any other function.
In that single case's expression, one can use variables defined in the scope which defines the anonymous function, creating a sort of closure.
If the left operand has commas, RTensor will break it down into a list of patterns, allowing for more than one parameter, rather than a single pattern.
### Currying
Using closures, one can make multi-argument anonymous functions a different way:
make a function that returns another function, as a partially-evaluated form of the desired multi-argument function — for example, `x => (y => x + y)` instead of `x, y => x + y`.
RTensor does not have a special syntax to facilitate the requisite chained method calls, so one would use the curried version not as `f(a, b)` but `f(a)(b)`.
## Built-in functions
In descriptions of functions, identifiers in parentheses or brackets (as appropriate) correspond to arguments for valid ways to call a function.
The exact identifier used indicates the expected type:
- `a`, `b`, `c`: number (in some contexts, specifically integer)
- `f`, `g`: function
- `k`, `m`, `n`: integer
- `t`: tensor (any rank above 0)
- `v`: vector (rank 1 tensor)
- `x`, `y`: number (rank 0 tensor)
- `w`, `z`: any type
Some functions work on a tensor, but only look one rank deep, using the tensor in place of a vector.
In this case, the parameter uses `t` for the identifier.
### Numeric/trigonometric functions
All functions in this list that accept a real number (so not `random`) will accept, in place of that number, a tensor, and will perform the same operation on each number in the tensor, returning an equivalently-structured tensor.
- `abs(x)`, the absolute value or magnitude of `x`
- `floor(x)`, the integer rounded down (towards negative infinity) from `x`
- `random()`, a random number in `[0, 1)`, or `random(m, n)`, a random integer in `[m, n]`
- `sin(x)`
- `cos(x)`
- `arcsin(y)`
- `arccos(y)`
- `arctan(y)`, or `arctan(y, x)`
- `re(x)`, the real component of complex number `x`
- `im(x)`, the imaginary component of complex number `x`
- `conj(x)`, the complex conjugate of `x`
### Tensor functions
- `zero(n)`, an `n`-element vector of zeroes
- `len(t)`, the number of elements in the top level of `t`
This does not count the number of numbers in `t`, but merely the number of tensors of lesser rank immediately composing `t`.
For example, `len(((4, 7), (2, 3), (8, 5)))` return 3 rather than 6.
- `any(v)`, the first nonzero value in `v`.
If `v` consists of `true`/`false` values, this functions as an inclusive OR.
- `all(v)`, the first zero-equivalent value in `v`.
If `v` consists of `true`/`false` values, this functions as an AND.
- `map(t, f)`, a new tensor (same rank as `t`, greater than 0) with each element `x` from `t` (in the top level) replaced with `f(x)`.
If `f` accepts two arguments (binary function), `map` will replace `x` with `f(x, i)`, where `i` matches the index of `x` from `t`.
If `f` does not accept any arguments (nullary function), `map` will replace `x` with `f()`.
Notice that `map` *only* acts on top-level elements;
e.g. `map(((4, 7), (2, 3), (8, 5)), f)` applies `f` to `(4, 7)`, `(2, 3)`, and `(8, 5)`, not `4`, `7`, `2`, `3`, `8`, `5`.
To apply a function to replace *every number* in a tensor, use `deepmap`.
- `filter(t, f)`, a new tensor (same rank as `t`, greater than 0) containing all elements `x` from `t` (in the top level) for which `f(x)` returns a value equivalent to `true`.
If `f` accepts two arguments (binary function), `filter` will copy `x` for which `f(x, i)` returns a value equivalent to `true`, where `i` matches the index of `x` from `t`.
As with `map`, `filter` *only* acts on top-level elements.
RTensor currently does not have any `deepfilter` to check `f(x)` for every number in a tensor.
- `reduce(t, f)`, a value computed by repeatedly applying `x = f(x, y)` for each element `y` of `t` (at the top level).
The initial value of `x` comes from the first element of `t`;
`reduce` does not apply `f(x, y)` using that initial value for `y`.
As with `map`, `reduce` *only* acts on top-level elements.
RTensor currently does not have any `deepreduce` to combine all individual numbers in a tensor.
- `tail(t)`, a new tensor consisting of all the elements of `t`, in the same order, except for the first
- `trim(t)`, a new tensor consisting of all the elements of `t`, in the same order, except for the last
- `deepmap(t, f)`, a new tensor with the same structure as `t`, but with every number `x` (down thru all ranks) replaced with `f(x)`
### Control flow
- `if(c, w, z)` or `if[c, w, z]`, a selection between `w` and `z` depending on `c`, selecting `w` iff `c` equates to `true`.
`if` accepts up to two extra arguments to automatically apply to the selected value as arguments (assuming that value has callable behaviour).
For more than two arguments (and recommended in general, as we may deprecate that feature), we suggest directly calling the result instead, as in `if(c, w, z)(a, b, x, y)`.
- `for(init, cond, step)`, the final value of `x` — which starts as `x = init` — after repeatedly applying `x = step(x)`, until `cond(x)` returns a value equivalent to `false`
### Other built-in functions
- `clear()`, which clears display but not history or variables
- `lhs[expr]`, the left side of `expr`
- `rhs[expr]`, the right side of `expr`
- `operator[expr]`, a number corresponding to the type of operator in `expr`
- `sum[(var = expr), n, expr]`, corresponding to typical summation notation
- `product[(var = expr), n, expr]`, likewise
- `render[expr]`, which (crudely) renders the expression in the webpage version
- `html[expr]`, which outputs HTML code to display the expression
- `latex[expr]`, which outputs L_{A}T_{E}X code to display the expression
### Non-function built-ins
- `i`, the imaginary unit
- `pi`
- `phi`, the golden ratio 1.618
- `true`
- `false`
- `null`
If you want `e`, use `^1`.