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