Expressions
#
Logic expressionsArr.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 expr2
evaluates toexpr1
iftestexpr
is "true", orexpr2
otherwise.expr1 && expr2
evaluates toexpr1
if it is "true" orexpr2
otherwise.expr1 || expr2
evaluates toexpr1
if it is "false" orexpr2
otherwise.
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 expressionsArr.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 expressionsTuple 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?:42
will 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 expressionsThe following operators bind name
to something related to expr1
(details
below) and evaluates expression expr2
with name
in scope.
let name = expr1; expr2
orexpr1 -> \name expr2
: Evaluatesexpr2
withexpr1
in scope asname
.expr1 => \name expr2
: Transforms each element of setexpr1
and evaluates to the set of results.expr1 >> \name expr2
: Transforms each item of keyed-collectionexpr1
and 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
: Bindsname
to each value in tupleexpr1
, evaluatesexpr2
and 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.
#
RelationsRelations 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.
#
FunctionsThere 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.fix
is 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.fixt
is a variant offix
that 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.
#
PackagesExternal 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.sin
and//math.pi
.//str
: string functions such as//str.upper
and//str.lower
.//fn
: higher order functions such as//fn.fix
and//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 eval
source that aren't associated with a file).//{/path}
provides access to other arrai files relative to the root of the current module, looking forgo.mod
file backwards from the current directory.//{hostname/path}
provides access to content from the internet//{github.com/foo/bar/baz}
: accessbaz.arrai
file in remote repositorygithub.com/foo/bar
//{github.com/foo/bar/a.json}
: accessa.json
file in remote repositorygithub.com/foo/bar
//{foo.org/bar/}'random.arrai'
///{https://foo.org/bar/random.arrai}
: request content ofhttps://foo.org/bar/random.arrai
via HTTPS//{foo.org/bar/some.json}
///{https://foo.org/bar/some.json}
: request content ofhttps://foo.org/bar/some.json
via HTTPS//{foo.org/bar/some.yaml}
///{https://foo.org/bar/some.yml}
: request content ofhttps://foo.org/bar/some.yaml
via HTTPS, file extension can beyml
oryaml