# Rel Primer: Advanced Syntax

Introducing more advanced Rel syntax.

# Introduction

This document assumes that you are familiar with Basic Rel Syntax, and introduces more advanced features of the Rel language.

## Grounded and Ungrounded Variables

We begin by considering what relations Rel can be expected to compute.

A variable is *grounded* when it can be instantiated to a specific, finite set of values.
For example, in the expression
`x: range(1, 100, 1, x)`

, using the built-in `range`

relation,
the variable `x`

is instantiated to the 100 different values `1, 2, 3, ..., 100`

to produce the relation.
Similarly, when we write `x : min(1, 2, x)`

, the variable `x`

is bound to `1`

.

By contrast, if we write `x: min(1, x, 1)`

, there are infinitely many values of `x`

for which `min(1,x,1)`

is true;
and if we write `x, y: range(1, y, 1, x)`

, there are an infinite number of possible values for `x`

and `y`

.
These variables are said to be *ungrounded*.

When the RAI server encounters one of these cases, you will see an error stating that the definition “contains ungrounded variable(s); hence, the rule cannot be evaluated.”

In general, if we write `range(x, y, z, result)`

, we should expect to know the values of `x, y, z`

, and say that
those values *ground* the value of `result`

.

`@inline`

Definitions

We can still declare and use definitions that are not expected to be fully computed, by using the `@inline`

keyword. For example:

Here, `mymax`

is not intended to be computed directly — we would not expect to enumerate all the tuples that satisfy it.
If it were not labeled as `@inline`

,
the Rel compiler would attempt to compute `mymax`

and complain that `x`

and `y`

are ungrounded.

You can add the `@inline`

annotation to any relation.
If you have a large derived relation but are only interested in querying it for particular values,
it can be best to inline it, so that the system does not compute all values in that relation.
Similarly, if the rest of your code only needs a few values.

Technical note: We expect the `@inline`

annotation
to become less important as the system matures and can make more of these
decisions automatically.

## Relations as Arguments

Relations in Rel are *first-order*, meaning they do not take relations as values.
But we can use `@inline`

to define *syntactically* “higher-order” relations, which
can take a relation as one of their arguments.

For example:

`@inline`

def maxmin[Relation] = (max[Relation], min[Relation])

def output = maxmin[ {1; 2; 3; 4; 5} ]

Relation: output

5 | 1 |

Here we used `max`

and `min`

from the stdlib, which take relations as their argument as well.

`@inline`

definition, variable names that correspond to relations must start with an uppercase letter,
as is the case with `Relation`

above.This does not contradict the first-order nature of the language: when the `@inline`

def is expanded,
the result is first-order again.
Readers with experience with programming languages
can think of `@inline`

definitions as macros that are expanded at compile-time.
They are not directly evaluated themselves, keeping things first-order.

`argmax`

is another stdlib utility that takes a relation `R`

as argument,
returning the rows in `R`

that have the largest value.
For example:

We can use `argmax`

on the sample data from
the aggregation Primer section
to find the highest-paid players for a particular team. Here are the `plays_for`

and `salary`

relations:

Relation: plays_for

"Busquets" | "BFC" |

"Carvajal" | "RM" |

"Cortois" | "RM" |

"Dembele" | "BFC" |

"Griezmann" | "BFC" |

"Kroos" | "RM" |

"Marcelo" | "RM" |

"Messi" | "BFC" |

"Modric" | "RM" |

"Pique" | "BFC" |

"Ramos" | "RM" |

"Umtiti" | "BFC" |

"Varane" | "RM" |

Relation: salary

"Busquets" | 15 |

"Carvajal" | 7 |

"Cortois" | 7 |

"Dembele" | 12 |

"Griezmann" | 45 |

"Kroos" | 10 |

"Marcelo" | 7 |

"Messi" | 70 |

"Modric" | 10 |

"Pique" | 12 |

"Ramos" | 15 |

"Umtiti" | 12 |

"Varane" | 7 |

`def output = argmax[name, s : plays_for(name, "BFC") and salary(name, s)]`

Relation: output

"Messi" |

It is easy to generalize this query to get the highest-paid player for each team that participates in the `plays_for`

relation:

`def output = team: argmax[name, s : plays_for(name, team) and salary(name, s)]`

Relation: output

"BFC" | "Messi" |

"RM" | "Ramos" |

### Example

Here’s an example of how `@inline`

functions can give give the code a higher-order flavor.
(We explain the `x...`

syntax in the Varargs section below).

`@inline`

def plusone[R][x...] = R[x...] + 1

def foo = (1, 3); (4, 5); (5, 6, 10)

def bar = plusone[foo]

def output = bar[4]

Relation: output

6 |

## Multiple Arities

Mathematically speaking, an individual relation has a fixed arity.
However, Rel lets us define relations with different arities but the same name.
This is sometimes (informally) known as *overloading*, where we can treat a set of relations as a single one.
For example:

The built-in predicate `arity`

can be used to check the arity, or arities, of a relation `R`

.
For example:

Thus, if `count[arity[R]] = 1`

, then `R`

is not overloaded by arity.

## Varargs

To write more general code that works for many different arities, Rel provides a *varargs* mechanism,
where `x...`

matches zero or more variables. For example, the `stdlib`

defines `first`

and `last`

as:

`@inline`

def first[R](x) = ∃(y... : R(x, y...))

@inline

def last[R](y) = ∃(x... : R(x..., y))

These could also be defined as:

`@inline`

def first[R](x) = R(x, y...) from y...

@inline

def last[R](y) = R(x..., y) from x...

As another example, in Basic Syntax
we discussed how `R[S]`

only works for unary relations `S`

.
We can write a more general version as follows:

`@inline`

def prefix_restrict[PREFIX, R] = R[x...] from x... in PREFIX

def foo = (1, 2, 3); (4, 5, 6); (7, 8, 9)

def r = {(1, 2); (4, 5)}

def output = prefix_restrict[r, foo]

Relation: output

3 |

6 |

(`prefix_restrict`

is closely related to the stdlib’s `prefix_join`

, aka `<:`

, discussed below.)

When using varargs, keep in mind that the system needs to compute the arity of any requested predicates.
For an overloaded relation, this should be a finite number of arities.
For example, the following query succeeds, and `foo(1)`

is `true`

:

When computing `output`

, the system sees that `foo`

has arity 1, and thus `vs...`

is an empty list of variables.

However, the following query fails; while `foo(1)`

constrains `foo`

to have exactly arity 1,
`foo[1]`

only constrains it to be non-zero, so `foo`

could have any
one of an infinite number of arities:

`@inline def foo(vs..., 1) = true`

def output:yes = foo[1]

### A Word About Arities

In general, we prefer relations with small arities, and working with normalized data, which keeps the arities small and helps with performance, readability, and correctness. Therefore, while varargs are very useful to write general utilities (see the Standard Library for examples), the arity of individual relations should not be too high. (Note how even wide CSV tables are ingested as a set of ternary relations, for example.)

## Multiple Types

Relations can also be overloaded by *type*, meaning that they can contain tuples with different types.
You can think of each combination of types as a separate relation.
A Rel expression can also refer to a combination of different arities and types.
For example:

## Specialization: RelNames (`:name`

)

Related to overloading by type, relations can be *specialized*, which separates them into different relations
depending on the particular values they take.

This is always the case for *RelNames*, which are values starting with a colon (`:`

), such as `:a`

and `:b`

below:
For example:

When using the RAI Console notebooks on your browser,
you will notice that they print out the specialized relations for `:a`

and for `:b`

separately.

Imported CSV relations are specialized by the column name, which is a symbol, so each column becomes a separate relation.

### RelNames and EDB Updates

We sometimes use the symbol `:relname`

to refer to the relation `relname`

, particularly when updating stored relations
(using `insert[:relname]=...`

and `delete[:relname]=...`

.
See the concept guide on Updating Data: Working with EDB Relations.

### RelNames and Modules

The correspondence between RelName symbols and specialized relations also shows up in modules,
where `module:foo`

is the same as `module[:foo]`

.
See the Rel Modules Concept Guide for more details.

## Recursion

Rel supports recursive definition, provided the recursion is well-founded (has a base case) and the relations computed are finite.

`def fib[0] = 0`

def fib[1] = 1

def fib[x in range[2,10,1]] = fib[x-1] + fib[x-2]

def output = fib

Relation: output

0 | 0 |

1 | 1 |

2 | 1 |

3 | 2 |

4 | 3 |

5 | 5 |

6 | 8 |

7 | 13 |

8 | 21 |

9 | 34 |

10 | 55 |

See the Recursion Concept Guide for more details.

*stratified*(meaning, roughly, no cyclic recursive definitions with a negation in the loop, as in

`def p = not p`

).## Multiple Rules Add Up

As mentioned in the Basic Syntax section, the definitions for any given relation are combined.

This is the case even if the definitions are in separate installed models or libraries. It can be useful to extend existing relations for new types or arities. This applies even to recursive definitions, where we can add new base cases or new recursive definitions to an existing one.

The following definition adds a new base case:

The order of the rules does not matter — and they can be separated by other rules, or even be installed separately.

Another interesting feature of this example is that while the first two rules for `rec`

were *installed*,
the extra base case was part of a *query*, which enables it only for the effects of that query itself.
This means that if we ask for `rec`

again,
we get only the values from the rules that were originally installed:

## Point-Free Syntax

Rel’s syntax supports *point-free* notation, where we can omit argument variables when
their omission does not lead to ambiguity.
For example, instead of writing:

`def mydomain(x) = range(1, 7, 1, x)`

we can write:

`def mydomain = range[1, 7, 1]`

Again, conjunctions correspond to `,`

and disjunctions to `;`

. For example:

`def myrel(x, y) = r1(x) and r2(y)`

can be expressed point-free as:

`def myrel = r1, r2`

In both cases, we get the cross-product of `r1`

and `r2`

.

To see how `;`

corresponds to `or`

:

`def myrel(x, y) = r1(x, y) or r2(x, y)`

is equivalent to:

`def myrel = r1 ; r2`

As another example, instead of:

we can use `intersect`

from the stdlib and write:

`def myrel = intersect[a, b]`

We can even use point-free recursive definitions, such as this one, for the transitive closure of the binary relation `r`

(see below for an explanation of the composition operator `.`

):

`def r = {(1,2); (2,3); (3,4); (2,5)}`

def tcr = r

def tcr = tcr.r // see section below for the meaning of "."

def output = tcr

Relation: output

1 | 2 |

1 | 3 |

1 | 4 |

1 | 5 |

2 | 3 |

2 | 4 |

2 | 5 |

3 | 4 |

Note that writing `def myrel = a and b`

will give an arity error if `a`

and `b`

do not have arity 0, which `and`

expects.
Similarly for `or`

, `not`

, and `implies`

.

## Relational Equality: `equal`

vs. `=`

In Rel, `=`

means equality between individual (scalar) values. (That is, when `=`

is used in a formula;
the “=” used in `def myrel = ...`

has a special status as a reserved symbol).
For example, `3 = 2 + 1`

is `true`

,
and writing `x = y`

supposes that `x`

and `y`

are individual values.

To test equality between *relations*, `equal`

should be used:

Since Rel distributes scalar operations over relation values, using `=`

for relations can give confusing results.
For example:

`// do not use `=` to check equality of relations!`

def output:wrong = if {1} = {1; 2; 3} then "equal" else "not equal" end

// use `equals` for relations:

def output:right = if equal({1}, {1; 2; 3}) then "equal" else "not equal" end

Relation: output

:right | "not equal" |

:wrong | "equal" |

Technical footnote: If `R1`

and `R2`

are relations, then `R1 = R2`

iff their intersection is non-empty.

`=`

to compare individual values, and `equal`

to compare relations.## Useful Relational Operators

The Rel Standard Library defines many useful relational operations.

This section describes some of the most commonly used relational operations.

### Composition (`.`

)

Relational *composition*, indicated by a dot (`.`

), is a shortcut for joining the last element of a relation
with the first element of another — usually a foreign key:

`def order_products = {(12, 3213) ; (10, 3213) ; (7, 9832)}`

def product_names = {(3213, "laptop"); (9832, "iphone"); (45353, "TV")}

def output = order_products.product_names

Relation: output

7 | "iphone" |

10 | "laptop" |

12 | "laptop" |

As another example, if `parent(x, y)`

holds when `x`

is a parent of `y`

, then `x : x.parent.parent`

is the “grandparent” relation:

`def parent = {("bill", "alice") ; ("alice" , "bob") ;`

("alice" , "mary") ; ("jane", "john")}

def output = "bill".parent.parent

Relation: output

"bob" |

"mary" |

### Prefix and Suffix Joins (`<:`

and `:>`

)

Prefix join, written as `prefix_join[R, S]`

or `R <: S`

,
generalizes `[]`

to get the elements of a relation `S`

that have a prefix in `R`

.
Using varargs notation,
`R <: S`

contains the tuples `(x..., y...)`

in `S`

where `(x...)`

is in `R`

.
For example:

`def r = {(1, 2); (2, 5)}`

def s = {(1, 2, 3); (1, 5, 7); (1, 2, 8); (2, 5, 9)}

def output = r <: s

Relation: output

1 | 2 | 3 |

1 | 2 | 8 |

2 | 5 | 9 |

`def json = parse_json["""{"a": {"b": 1, "c": 2}, "d": 3}"""]`

def output = :a <: json

Relation: output

:a | :b | 1 |

:a | :c | 2 |

Suffix join, written as `suffix_join[S, R]`

or `S :> R`

is similar, but matches suffixes.
`R :> S`

contains the tuples `(x..., y...)`

in `R`

where `(y...)`

is in `S`

:

`def r = {(1, 2, 3); (1, 5, 7); (1, 2, 8); (2, 5, 9)}`

def s = {(2, 3); (5, 9)}

def output = r :> s

Relation: output

1 | 2 | 3 |

2 | 5 | 9 |

`@inline def even(x) = (x%2=0)`

def json = parse_json["""[ {"a": 1, "b": 2}, {"a": 3, "b": 4, "c": 6} ]"""]

def output = json :> even

Relation: output

:[] | 1 | :b | 2 |

:[] | 2 | :b | 4 |

:[] | 2 | :c | 6 |

(Here, the prefix `([], n)`

indicates a JSON list, with `n`

as the index. For details on how JSON is represented in Rel,
see the JSON topic guide.)

### Left and Right Override (`<++`

and `++>`

)

The (left) override operator is usually applied to relations of tuples `(k..., v)`

with a functional
dependency from the initial (key) arguments `k...`

to the last argument `v`

(the value).
It lets us merge two such relations, giving precedence to one over the other.

`left_override[R, S]`

, also written as `R <++ S`

,
contains all the tuples `(x..., v)`

of `R`

, plus all the tuples `(x1..., v1)`

of `S`

where `x1...`

, is not in `R`

.
Often, `S`

specifies default values for keys that are missing in `R`

.

As a mnemonic, when we read `R <++ S`

we can imagine `S`

injecting new values into `R`

, but only when those keys are missing (so `R`

is overriding `S`

).

`def base = ("a", 2) ; ("b", 4)`

def defaultvalues = ("a", 10) ; ("c", 20)

def combined = (base <++ defaultvalues)

def output = combined

Relation: output

"a" | 2 |

"b" | 4 |

"c" | 20 |

A common use case for override is making explicit default values,
using a constant relation for the default.
We can write `count[R] <++ 0`

if we want the
count of an empty relation to be
0 rather than empty. As another example, an EDB counter can be defined as:

(Note that without the `delete`

, the consecutive values will accumulate in `counter`

, rather than replace each other.
See the Concept Guide on Updating Data for details.)

Here is an example of how we can add defaults for a domain,
with a definition using `in`

:

`def base = ("a", 10)`

def mydomain = ("a" ; "b" ; "c")

def filled[x in mydomain] = base[x] <++ 0

def output = filled

Relation: output

"a" | 10 |

"b" | 0 |

"c" | 0 |

Rel also provides `++>`

(`right_override`

), which swaps the order of the arguments.

As a special case, `count[R] <++ 0`

gives us a count of zero for empty sets
(see Aggregating over the Empty Set).

## Types

Rel provides unary relations for testing the type of primitive values.
These tests include `String`

, `Number`

, `Int`

, and `Float`

,
which take one argument, and counterparts for fixed-width types, such as
`Floating`

. Example: `Floating[32, float[32, 3.0]]`

is `true`

.

See the Data Types Reference for details on all of the primitive types.

Types are particularly useful for enforcing schemas as integrity constraints. For example,

`ic { subset(myrel, (Int, Float, String) ) }`

will make sure that `myrel`

has the given type signature (`Int x Float x String`

).

This works for both EDBs and IDBs. If `myrel`

is an EDB, then any `insert`

that tries to add something of the wrong type
will fail. If `myrel`

is an IDB, then any database update that would cause it to be populated by the wrong type will also fail.

## Summary

This article has covered more advanced features of Rel, including higher-order definitions, overloading by arity and types, varargs, specialization and point-free syntax.

## Further Reading

We have separate documents explaining important concepts in Rel:

See the Rel Libraries for many more useful relational operators and utilities.