Binding environment for Datalog rule evaluation.
A binding maps variable names (strings) to ground values. During rule evaluation, each body atom extends the binding by matching tuple values against atom terms. Constraints filter or extend bindings further.
Types of resolution
- Variable (
{:var, "X"}) — looked up in the binding. If absent, the variable is unbound and the constraint/atom cannot be evaluated. - Constant (
{:const, {:int, 42}},{:const, {:atom, :alice}}, etc.) — resolved to the native Elixir value. - Wildcard (
:wildcard) — matches any value without binding.
Merge semantics
When joining two atoms that share variables, two bindings are merged. For shared variables the values must agree; otherwise the merge fails and the candidate tuple is discarded.
Summary
Functions
Binds a variable to a value in the environment.
Checks whether two binding environments are consistent on shared variables.
Creates an empty binding environment.
Looks up a variable in the binding environment.
Converts an IR value tag to its native Elixir value.
Merges two binding environments.
Resolves an IR term to a native Elixir value using a binding environment.
Types
Functions
Binds a variable to a value in the environment.
Examples
iex> ExDatalog.Engine.Binding.bind(%{}, "X", :alice)
%{"X" => :alice}
iex> ExDatalog.Engine.Binding.bind(%{"X" => :alice}, "Y", :bob)
%{"X" => :alice, "Y" => :bob}
Checks whether two binding environments are consistent on shared variables.
Returns true if every variable present in both bindings has the same value,
false otherwise.
Examples
iex> ExDatalog.Engine.Binding.consistent?(%{"X" => 1}, %{"X" => 1, "Y" => 2})
true
iex> ExDatalog.Engine.Binding.consistent?(%{"X" => 1}, %{"X" => 2})
false
iex> ExDatalog.Engine.Binding.consistent?(%{"X" => 1}, %{"Y" => 2})
true
@spec empty() :: t()
Creates an empty binding environment.
Examples
iex> ExDatalog.Engine.Binding.empty()
%{}
Looks up a variable in the binding environment.
Returns {:ok, value} if bound, :error if not.
Examples
iex> ExDatalog.Engine.Binding.get(%{"X" => :alice}, "X")
{:ok, :alice}
iex> ExDatalog.Engine.Binding.get(%{"X" => :alice}, "Y")
:error
@spec ir_value_to_native(ExDatalog.IR.ir_value()) :: term()
Converts an IR value tag to its native Elixir value.
Used internally by resolve/2 and by the evaluator for fact conversion.
Examples
iex> ExDatalog.Engine.Binding.ir_value_to_native({:int, 42})
42
iex> ExDatalog.Engine.Binding.ir_value_to_native({:str, "hello"})
"hello"
iex> ExDatalog.Engine.Binding.ir_value_to_native({:atom, :alice})
:alice
Merges two binding environments.
For shared variables, the values must be equal (using ==/2). If any
shared variable disagrees, returns :conflict. Otherwise returns
{:ok, merged_binding}.
Examples
iex> ExDatalog.Engine.Binding.merge(%{"X" => 1}, %{"Y" => 2})
{:ok, %{"X" => 1, "Y" => 2}}
iex> ExDatalog.Engine.Binding.merge(%{"X" => 1}, %{"X" => 1, "Y" => 2})
{:ok, %{"X" => 1, "Y" => 2}}
iex> ExDatalog.Engine.Binding.merge(%{"X" => 1}, %{"X" => 2})
:conflict
@spec resolve(t(), ExDatalog.IR.ir_term()) :: {:ok, term()} | :unbound | :wildcard
Resolves an IR term to a native Elixir value using a binding environment.
{:var, name}— looks up in binding. Returns{:ok, value}if bound,:unboundif not.{:const, ir_value}— returns{:ok, native_value}unconditionally.:wildcard— returns:wildcard(matches anything, cannot be resolved).
Examples
iex> ExDatalog.Engine.Binding.resolve(%{"X" => 42}, {:var, "X"})
{:ok, 42}
iex> ExDatalog.Engine.Binding.resolve(%{}, {:var, "X"})
:unbound
iex> ExDatalog.Engine.Binding.resolve(%{}, {:const, {:int, 10}})
{:ok, 10}
iex> ExDatalog.Engine.Binding.resolve(%{}, {:const, {:atom, :alice}})
{:ok, :alice}
iex> ExDatalog.Engine.Binding.resolve(%{}, :wildcard)
:wildcard