THIS POST IS ARCHIVED

Varargs in Rel

We are excited to announce the support of varargs in Rel.

You can use varargs to write more general code that works for multiple arities. A vararg is written by appending three dots to a variable name, for instance, x..., which matches zero or more variables.

Varargs can be useful when writing generic relations for common utilities.

For instance, say you want to define a relation that contains tuples of differing arities. Instead of overloading its declaration several times, you can simply use varargs.

When using varargs, keep in mind that the system needs to compute the arity of the passed arguments. For an overloaded relation, this should be a finite number of arities.

The Rel Standard Library includes the relation first that returns the first argument of R. It is defined using varargs as:

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

In this way, the argument R can have any arity:

def R = {(1, 2, 3); (4, 5, 6)}
def output = first[R]

Output:

This works even if R contains tuples of various lengths and data types:

def R = {(1, 2, 3, 4); ("foo", "bar")}
def output = first[R]

Output:

Varargs work for point-wise definitions.

For instance, say you want to check whether two relations are the same. You can do that in a point-free manner by using equal from the Rel Standard Library as:

// read query

def foo = {(1, 2); (3, 4)}
def bar = {(1, 3); (3, 4)}

def output = equal(bar, foo)

The output returns false, which is equivalent to a zero-arity relation.

Now, let's check whether there is a tuple that is contained in both relations. For that, you can use varargs using point-wise syntax as follows:

// read query

def foo = {(1, 2); (3, 4)}
def bar = {(1, 3); (3, 4)}

def output = foo(x...) and bar(x...) from x...

In this case, the second tuples from both relations are equal and therefore the output is true.

The constant true is equivalent to {()}, which is the zero-arity relation that contains the empty tuple.

Finally, say you want to find tuples and their arities that occur both in foo and bar relations, without determining in advance what those arities might be. Again, you can do this with varargs:

def foo = {("Tom", 25); ("Alice", 34, 175); ("Bob", 20, 180)}
def bar = {("John", 19, 185); ("Bob", 20, 180); ("Tom", 25)}

def output(n, x... in foo) {
       arity[(x...)] = n and bar(x...)
}

Output:

While varargs are very useful for generalizing your code, relations with small arities that are normalized in Graph Normal Form are preferred. This helps with performance, readability, and correctness.