Expressions
Logic expressions
Arr.ai supports operations on "true" and "false" values. The values 0, ()
and {} are considered "false", while all other values are "true".
expr1 if testexpr else expr2evaluates toexpr1iftestexpris "true", orexpr2otherwise.expr1 && expr2evaluates toexpr1if it is "true" orexpr2otherwise.expr1 || expr2evaluates toexpr1if it is "false" orexpr2otherwise.
All above expressions exhibit short-circuit behaviours, which means that that
expr2 will be evaluated if its value is needed. While the arr.ai language has
no side-effects, short-circuit behaviour is still needed to terminate recursion.
Arithmetic expressions
Arr.ai supports operations on numbers.
- Unary:
+,- - Binary:
- Well known:
+,-,*,/,%(modulo),^(power) - Modulo-truncation:
-%(x -% y = x - x % y)
- Well known:
- Comparison operators, which may be chained:
0 <= i < 10- Set membership is treated the same:
10 <= n <: validIds.
- Set membership is treated the same:
Structure access expressions
-
Tuple attribute:
tuple.attr(string syntax is allowed, e.g.:('👋': 42)."👋")) -
Dot variable attribute:
.attr(shorthand for(.).attr) -
Function call:
[2, 4, 6, 8](2) = 6,"hello"(1) = 101{"red": 0.3, "green": 0.5, "blue", 0.2}("green") = 0.5
-
Conditional accessor syntax: allows for failures in accessing a tuple attribute or a set call, falling back on a provided expression. Any call or attribute access that ends with
?are allowed to fail.(a: 1).b?:42 = 42(a: 1).a?:42 = 1{"a": 1}("b")?:42 = 42{"a": 1}("a")?:42 = 1
It also allows for appending access expressions:
(a: {"b": (c: 2)}).a?("b").c?:42 = 2(a: {"b": (c: 2)}).a?("b").d?:42 = 42
Not all access failures are allowed: only missing attributes of a tuple, or a set call does not return exactly one value.
(a: (b: 1)).a?.b.c?:42will fail as it will try to evaluate1.c?:42.
-
Function slice: (⛔ NYI)
[1, 1, 2, 3, 5, 8](2:5) = [2, 3, 5][1, 2, 3, 4, 5, 6](1:5:2) = [2, 4]
Binding expressions
The following operators bind name to something related to expr1 (details
below) and evaluates expression expr2 with name in scope.
let name = expr1; expr2orexpr1 -> \name expr2: Evaluatesexpr2withexpr1in scope asname.expr1 => \name expr2: Transforms each element of setexpr1and evaluates to the set of results.expr1 >> \name expr2: Transforms each item of keyed-collectionexpr1and evaluates to the key-collection of results, with each result being associated with the same key that the original item was. This works for any binary relation with an@attribute, which includes strings, arrays, functions and other structures.expr1 :> \name expr2: Bindsnameto each value in tupleexpr1, evaluatesexpr2and reassociates each result with the corresponding name, producing a new tuple.
If expr1 is omitted in any of the arrow forms, . is assumed.
If \name is omitted, \. is assumed.
Relations
Relations are sets of tuples with a common set of names across all tuples. They are analogous to SQL tables. Numerous relational operators exist that work on these structures.
Functions
There are several flavors of functions. All functions are binary relations with
one attribute called @. The other attribute can have any name, including the
empty name, ''. The following are some examples of functions.
- Strings:
"hello"(2) = 108(l) - Arrays:
[10, 15, 20, 25, 30](3) = 25 - Lambda functions:
\x 2 * x
Unlike most other languages, arr.ai are no concept of named functions, either at
file level or any other scope. All functions are anonymous. A function can, of
course, be bound to a name via let or ->, but, since it cannot refer to this
name at the moment of assignment, this presents a challenge for implementing
recursion. This problem is solved by a couple of functions in the standard
library:
-
//fn.fixis a fixed-point combinator. It is typically used to transform non-recursive functions into recursive ones, e.g.:let factorial = //fn.fix(\factorial (\n (1 if n < 2 else n * factorial(n - 1))));
factorial(6) -
//fn.fixtis a variant offixthat operates on tuples of functions instead of a single function. This allows mutual recursion, e.g.:let eo = //fn.fixt((
even: \t \n n == 0 || t.odd (n - 1),
odd: \t \n n != 0 && t.even(n - 1),
));
eo.even(6)
However, these functions are also available through the syntactic sugar in the following syntax:
- For regular recursive functions:
let rec factorial = \n 1 if n < 2 else n * factorial(n - 1); factorial(5)
- For mutual recursion:
let rec oe = (
even = \n n == 0 || oe.odd (n - 1),
odd = \n n != 0 && oe.even(n - 1),
);
oe.even(6)
It is also possible to use the same syntax in a tuple.
let t = (
rec fact: \n cond n ((0, 1): 1, n: n * fact(n - 1)),
n : 5
);
t.rec(t.n)
This syntactic sugar only works with expression that evaluates to either a function or a tuple of functions. Anything else and the expression will fail.
Packages
External libraries may be accessed via package references.
//Is the root of the standard library. It provides access to many packages providing a wide range of useful capabilities. The following is a small sample of the full set://math: math functions and constants such as//math.sinand//math.pi.//str: string functions such as//str.upperand//str.lower.//fn: higher order functions such as//fn.fixand//fn.fixt. See the standard library reference for full documentation on all packages.
//{./path}provides access to other arrai files relative to the current arrai file's parent directory (current working directory for expressions such as thearrai evalsource that aren't associated with a file).//{/path}provides access to other arrai files relative to the root of the current module, looking forgo.modfile backwards from the current directory.//{hostname/path}provides access to content from the internet//{github.com/foo/bar/baz}: accessbaz.arraifile in remote repositorygithub.com/foo/bar//{github.com/foo/bar/a.json}: accessa.jsonfile in remote repositorygithub.com/foo/bar//{foo.org/bar/}'random.arrai'///{https://foo.org/bar/random.arrai}: request content ofhttps://foo.org/bar/random.arraivia HTTPS//{foo.org/bar/some.json}///{https://foo.org/bar/some.json}: request content ofhttps://foo.org/bar/some.jsonvia HTTPS//{foo.org/bar/some.yaml}///{https://foo.org/bar/some.yml}: request content ofhttps://foo.org/bar/some.yamlvia HTTPS, file extension can beymloryaml