ExDatalog.Rule (ExDatalog v0.2.0)

Copy Markdown View Source

A Datalog rule: head :- body [, constraints].

A rule consists of:

  • A head atom (ExDatalog.Atom.t()) — the relation being derived.
  • A body list of literals — each is one of:
    • {:positive, ExDatalog.Atom.t()} — a positive relational atom.
    • {:negative, ExDatalog.Atom.t()} — a negated relational atom.
  • A constraints list of ExDatalog.Constraint.t() — built-in predicates (comparisons and arithmetic) evaluated after joining body atoms.

Polarity is carried at the rule level, not in the atom itself. Constraints are kept separate from the body literal list for clarity and evaluation ordering.

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("ancestor", [Term.var("X"), Term.var("Y")])
iex> body = [{:positive, Atom.new("parent", [Term.var("X"), Term.var("Y")])}]
iex> Rule.new(head, body)
%ExDatalog.Rule{
  head: %ExDatalog.Atom{relation: "ancestor", terms: [{:var, "X"}, {:var, "Y"}]},
  body: [{:positive, %ExDatalog.Atom{relation: "parent", terms: [{:var, "X"}, {:var, "Y"}]}}],
  constraints: []
}

Summary

Functions

Returns all body atoms (stripping polarity).

Returns true if the rule contains any negative body literals.

Returns all variable names that appear in the rule head.

Constructs a new rule with a head atom and body literals.

Returns all variable names that appear in positive body atoms.

Returns all variable names that appear anywhere in the rule (head + body + constraints).

Types

literal()

@type literal() :: {:positive, ExDatalog.Atom.t()} | {:negative, ExDatalog.Atom.t()}

t()

@type t() :: %ExDatalog.Rule{
  body: [literal()],
  constraints: [ExDatalog.Constraint.t()],
  head: ExDatalog.Atom.t()
}

Functions

body_atoms(rule)

@spec body_atoms(t()) :: [ExDatalog.Atom.t()]

Returns all body atoms (stripping polarity).

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> a1 = Atom.new("parent", [Term.var("X"), Term.var("Y")])
iex> a2 = Atom.new("alive", [Term.var("X")])
iex> rule = Rule.new(Atom.new("ok", [Term.var("X")]), [{:positive, a1}, {:negative, a2}])
iex> Rule.body_atoms(rule)
[
  %ExDatalog.Atom{relation: "parent", terms: [{:var, "X"}, {:var, "Y"}]},
  %ExDatalog.Atom{relation: "alive", terms: [{:var, "X"}]}
]

has_negation?(rule)

@spec has_negation?(t()) :: boolean()

Returns true if the rule contains any negative body literals.

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("bachelor", [Term.var("X")])
iex> body = [
...>   {:positive, Atom.new("male", [Term.var("X")])},
...>   {:negative, Atom.new("married", [Term.var("X"), :wildcard])}
...> ]
iex> Rule.has_negation?(Rule.new(head, body))
true

iex> alias ExDatalog.{Rule, Atom, Term}
iex> Rule.has_negation?(Rule.new(
...>   Atom.new("r", [Term.var("X")]),
...>   [{:positive, Atom.new("s", [Term.var("X")])}]
...> ))
false

head_variables(rule)

@spec head_variables(t()) :: [String.t()]

Returns all variable names that appear in the rule head.

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("ancestor", [Term.var("X"), Term.var("Z")])
iex> body = [{:positive, Atom.new("parent", [Term.var("X"), Term.var("Y")])}]
iex> Rule.head_variables(Rule.new(head, body))
["X", "Z"]

new(head, body, constraints \\ [])

@spec new(ExDatalog.Atom.t(), [literal()], [ExDatalog.Constraint.t()]) :: t()

Constructs a new rule with a head atom and body literals.

Body literals must be {:positive, atom} or {:negative, atom} tuples. Constraints default to an empty list.

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("reachable", [Term.var("X"), Term.var("Y")])
iex> body = [{:positive, Atom.new("edge", [Term.var("X"), Term.var("Y")])}]
iex> Rule.new(head, body)
%ExDatalog.Rule{
  head: %ExDatalog.Atom{relation: "reachable", terms: [{:var, "X"}, {:var, "Y"}]},
  body: [{:positive, %ExDatalog.Atom{relation: "edge", terms: [{:var, "X"}, {:var, "Y"}]}}],
  constraints: []
}

positive_body_variables(rule)

@spec positive_body_variables(t()) :: [String.t()]

Returns all variable names that appear in positive body atoms.

These are the "safe" bindings available for use in the head and constraints.

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("result", [Term.var("X")])
iex> body = [
...>   {:positive, Atom.new("a", [Term.var("X"), Term.var("Y")])},
...>   {:negative, Atom.new("b", [Term.var("Y")])}
...> ]
iex> Rule.positive_body_variables(Rule.new(head, body))
["X", "Y"]

variables(rule)

@spec variables(t()) :: [String.t()]

Returns all variable names that appear anywhere in the rule (head + body + constraints).

Examples

iex> alias ExDatalog.{Rule, Atom, Term}
iex> head = Atom.new("ancestor", [Term.var("X"), Term.var("Z")])
iex> body = [
...>   {:positive, Atom.new("parent", [Term.var("X"), Term.var("Y")])},
...>   {:positive, Atom.new("ancestor", [Term.var("Y"), Term.var("Z")])}
...> ]
iex> rule = Rule.new(head, body)
iex> Rule.variables(rule) |> Enum.sort()
["X", "Y", "Z"]