Constraint evaluation for Datalog rule bodies.
Applies IR constraints (comparisons and arithmetic) to binding environments. Constraints are processed sequentially in the order they appear in the rule body. This sequential evaluation is required because arithmetic constraints bind their result variable, making it available to later constraints.
Evaluation dispatches through the ExDatalog.Constraint behaviour to
constraint-specific modules. The context parameter carries storage
capabilities from the engine so that constraint implementations can inspect
backend properties if needed.
Comparison constraints
Comparison constraints (:gt, :lt, :gte, :lte, :eq, :neq) are
filters: they keep or discard the current binding. If the comparison
evaluates to false, the binding is discarded (:filter). If true, the
binding passes through unchanged.
All variables in a comparison constraint must be bound before evaluation.
If any input variable is unbound, the binding is discarded (:filter).
Arithmetic constraints
Arithmetic constraints (:add, :sub, :mul, :div) bind their result
variable. The left and right operands must be bound; result is then
computed and added to the binding.
Division by zero returns :filter rather than raising.
Summary
Functions
Applies a list of IR constraints to a binding environment.
Applies a single IR constraint to a binding environment.
Types
@type binding() :: ExDatalog.Engine.Binding.t()
Functions
@spec apply( [ExDatalog.IR.Constraint.t()], binding(), ExDatalog.Constraint.Context.t() ) :: {:ok, binding()} | :filter
Applies a list of IR constraints to a binding environment.
Processes constraints sequentially. A comparison constraint that evaluates
to false discards the binding. An arithmetic constraint extends the binding
with its result variable.
Returns {:ok, final_binding} if all constraints pass, or :filter if any
comparison fails or an unbound variable is encountered.
Examples
iex> alias ExDatalog.Engine.ConstraintEval
iex> alias ExDatalog.IR.Constraint, as: C
iex> c1 = %C{op: :gt, left: {:var, "X"}, right: {:var, "Y"}, result: nil}
iex> ConstraintEval.apply([c1], %{"X" => 10, "Y" => 3})
{:ok, %{"X" => 10, "Y" => 3}}
iex> ConstraintEval.apply([c1], %{"X" => 3, "Y" => 10})
:filter
iex> c2 = %C{op: :add, left: {:var, "X"}, right: {:var, "Y"}, result: {:var, "Z"}}
iex> ConstraintEval.apply([c2], %{"X" => 3, "Y" => 7})
{:ok, %{"X" => 3, "Y" => 7, "Z" => 10}}
@spec apply_one( ExDatalog.IR.Constraint.t(), binding(), ExDatalog.Constraint.Context.t() ) :: {:ok, binding()} | :filter
Applies a single IR constraint to a binding environment.
Dispatches through ExDatalog.Constraint.evaluate/3 to the appropriate
constraint module based on the constraint's op field.
Returns {:ok, extended_binding} for a passing comparison or a successful
arithmetic binding. Returns :filter for a failing comparison, division by
zero, or an unbound input variable.