Constraints extend Datalog rules beyond simple pattern matching. They appear in rule bodies alongside relational atoms and filter or extend bindings based on computed conditions.

Constraint Types

Comparison Constraints

Comparison constraints filter bindings. Both left and right must be bound before evaluation. The result field is nil.

ConstructorMeaning
gt/2left > right
lt/2left < right
gte/2left >= right
lte/2left <= right
eq/2left == right
neq/2left != right

Arithmetic Constraints

Arithmetic constraints bind a result variable. left and right must be bound; after evaluation, result is added to the binding environment. All arithmetic is integer-only. Division by zero filters the binding.

ConstructorMeaning
add/3result = left + right
sub/3result = left - right
mul/3result = left * right
div/3result = div(left, right)

Type Predicate Constraints

Unary filters that check the Elixir type of a bound value. right and result are nil.

ConstructorMeaning
type_integer/1value is an integer
type_binary/1value is a binary (string)
type_atom/1value is an atom

String Predicate Constraints

Binary filters for string operations. Both operands must be bound and resolve to binaries. Returns :filter if either operand is unbound or not a binary.

ConstructorMeaning
starts_with/2String.starts_with?(left, right)
contains/2String.contains?(left, right)

Membership Constraints

Tests whether a value is in a constant list. left must be bound; right must be a constant list {:const, [values]}.

ConstructorMeaning
member/2left in right (list)

Evaluation

All constraints are evaluated through ExDatalog.Constraint.evaluate/3, which dispatches to the appropriate module based on the constraint's op field. The dispatch table is closed — adding a new constraint type requires editing the constraint_module/1 function in ExDatalog.Constraint.

The evaluation context (ExDatalog.Constraint.Context) carries storage backend capabilities. For v0.2.0, no constraint implementation reads from it, but the plumbing is complete for future use.

Terms

Constraints use IR terms for their operands:

  • {:var, "X"} — a variable, looked up in the binding
  • {:const, {:int, 42}} — a constant integer
  • {:const, {:atom, :alice}} — a constant atom
  • {:const, {:str, "hello"}} — a constant string
  • :wildcard — matches any value without binding