Skip to content
This site is for a previous version of RelationalAI.


Every value in Rel is a first-order relation, that is, it is an unordered set of tuples. The tuples are ordered sequences of data values. See Data Types for extensive information about various types of data values.

You can write tuples as data values separated by commas (v1, v2, ...), and write relations as tuples separated by semicolons (;) and wrapped in curly braces ({}). For example:

{(1, 2); (3, 4); (5, 6)} is a relation with arity 2 and cardinality 3. It can be visualized as the following table:


{(1, 2, 3); (4, 5, 6)} is a relation with arity 3 and cardinality 2. It can be visualized as the following table:


{(1); (2); (3); (4)} is a relation with arity 1 (a unary relation) and cardinality 4. It can be visualized as the following table:


Parentheses are typically left out for unary relations, so this relation can be written as {1; 2; 3; 4}.

The purpose of this first section is mainly to explain what notation you can use for values computed by Rel; but the notation is also supported by the Rel language itself.

In Rel, the parentheses of the tuple are in general optional because , is an operator and it binds more strongly than the ; operator. See Comma and Semicolon. Just as in many other languages, tuples with a trailing comma are allowed, as in {(1,); (2,); (3,); (4,)}.

A tuple is always a “flat” sequence of data values, even though in Rel you can write expressions within tuples. So (1, 2 + 1, 2 + 3) is equivalent to (1, 3, 5).

Moreover, additional parentheses do not matter. For example, ((1, 3), ((5))) is equivalent to (1, 3, 5). Similarly, (1, 3, ()) is equivalent to (1, 3).

As mentioned above, every value in Rel is a first-order relation. This means, in particular, that:

  • Relations cannot contain relations. Relations can contain tuples whose elements are data values such as integers, floating-point numbers, fixed-point decimals, characters, dates, datetimes, etc. Some of the data values — dates, for instance — have internal structure, but each of them has a fixed size. The Rel system is designed to support data values of arbitrary fixed-size Julia types.
  • A stand-alone data value, such as an integer or a string, always represents a singleton, that is, a relation that contains just one tuple. For example, 5 outside a tuple is just a shorthand notation for {(5,)}. Similarly, "what?" outside a tuple is equivalent to {("what?",)}.

The Type of a Relation

Unlike a mathematical relation, a Rel relation can contain tuples of different lengths, that is, its arity does not have to be fixed. A relation whose arity is not fixed can be thought of as a convenient wrapper for several different mathematical relations. In fact, the physical representation of such a relation consists of multiple conventional relations of fixed arity.

The remarks above apply also to the type of the elements in the tuples of a relation. For instance, if a Rel relation of arity 1 contains both integers and strings, then it represents two different physical relations.

In this manual the term type, when applied to a relation, will refer to the combination of arity and the types of the elements in the relation’s tuples. The type of a relation can be represented by the Cartesian product. For instance, the fact that a relation has arity 2, and each of its tuples consists of an integer followed by a string, can be succinctly expressed by saying that the relation is a subset of Int, String. This notation is used, in particular, in bound declarations.

In general, a Rel relation can be a wrapper for a number of relations, each of which has a different type. The wrapper is constructed by giving the same name to all these relations. See Definitions.

Variables, Singletons, and Data Values

Before reading this section, you might want to acquaint yourself with the rest of the manual, in particular with Relational Application.

A variable normally represents a singleton relation that contains a data value. Strictly speaking, the value of a variable x is a singleton, but it is often convenient to say that the value of x is the data value contained in the singleton.

For convenience, conversion between singletons and data values is often carried out behind the scenes. Consider, for instance, the following:

// read query
def output(x, y, z) = {(3, 4)}(x, y) and z = x + y and equal(z, {(7,)})

In the example above there are several instances of such conversions:

  • In {(3, 4)}(x, y) the data values 3 and 4 are extracted from the tuple (3, 4) and converted to singletons. The variable x now represents {(3,)} and y represents {(4,)}.
  • In z = x + y the data values 3 and 4 are extracted from x and y and added. The result is converted to the singleton {(7,)} represented by z.

Notice that z is a relation, because equal(z, {(7,)}) evaluates to true, and equal is defined only for relations.

Next: Lexical Syntax

Was this doc helpful?