ExDatalog.Engine.Binding (ExDatalog v0.2.0)

Copy Markdown View Source

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

t()

@type t() :: %{required(String.t()) => term()}

Functions

bind(binding, var_name, value)

@spec bind(t(), String.t(), term()) :: t()

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}

consistent?(b1, b2)

@spec consistent?(t(), t()) :: boolean()

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

empty()

@spec empty() :: t()

Creates an empty binding environment.

Examples

iex> ExDatalog.Engine.Binding.empty()
%{}

get(binding, var_name)

@spec get(t(), String.t()) :: {:ok, term()} | :error

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

ir_value_to_native(arg)

@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

merge(b1, b2)

@spec merge(t(), t()) :: {:ok, t()} | :conflict

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

resolve(binding, arg2)

@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, :unbound if 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