Clause storage: {name, arity} -> ordered list of {head, body}
clauses, body a single term (true for a fact, matching real
Prolog's own internal Head :- Body shape) -- looked up by
Episteme.Engine on every user-predicate call, and mutated in place by
Episteme.Engine's assert/asserta/assertz/retract/retractall
dispatch. Populated either directly (add_clause/2/add_fact/2,
building terms yourself with Episteme.Term) or via consult_forms/2,
for any front-end that parses its own surface syntax into
{:fact, _}/{:rule, _, _} forms (Aletheia's reader is one such
front-end, but this module has no dependency on it or any particular
concrete syntax).
Storage itself is pluggable via Episteme.Database.Backend: new/1
defaults to Episteme.Database.Backends.Ets (in-memory, indexed by
{name, arity} -- O(1) lookup instead of a linear scan, and mutable
in place so assert/retract effects are visible to every holder of
the same t() value, including across separate Episteme.query/2
calls). Pass backend: Episteme.Database.Backends.Dets, file: path for
a database that persists to disk, or any other module implementing
Episteme.Database.Backend.
Because a t() now wraps a mutable resource (an ETS table, an open
DETS file, ...) rather than being a plain immutable value, call
close/1 when you're done with a database you don't want to leak --
an ETS-backed one is cleaned up automatically if its owning process
exits, but a DETS-backed one holds an open file handle until closed.
Summary
Functions
Like add_clause/2, but prepends -- the primitive asserta/1 builds on.
Releases whatever resources the backend holds (an ETS table, an open DETS file, ...).
Loads every {:fact, head}/{:rule, head, body} form into db.
{:directive, _} forms are ignored here -- a front-end reader's own
directives (an op/3 table mutation, say) are that front-end's
concern, not the database's.
Builds an empty database. opts[:backend] (default
Episteme.Database.Backends.Ets) picks the storage backend; any other
option is backend-specific and passed straight to its init/1 (e.g.
:file for Episteme.Database.Backends.Dets).
Replaces the entire clause list for {name, arity} -- the primitive Episteme.Engine's retract/1/retractall/1 build on.
Forces any buffered writes to durable storage. A no-op on the default in-memory backend.
Types
@type indicator() :: {atom(), non_neg_integer()}
@type t() :: %Episteme.Database{ backend: module(), state: Episteme.Database.Backend.state() }
Functions
Like add_clause/2, but prepends -- the primitive asserta/1 builds on.
@spec clauses_for(t(), atom(), non_neg_integer()) :: [clause()]
@spec close(t()) :: :ok
Releases whatever resources the backend holds (an ETS table, an open DETS file, ...).
Loads every {:fact, head}/{:rule, head, body} form into db.
{:directive, _} forms are ignored here -- a front-end reader's own
directives (an op/3 table mutation, say) are that front-end's
concern, not the database's.
@spec defined?(t(), atom(), non_neg_integer()) :: boolean()
Builds an empty database. opts[:backend] (default
Episteme.Database.Backends.Ets) picks the storage backend; any other
option is backend-specific and passed straight to its init/1 (e.g.
:file for Episteme.Database.Backends.Dets).
@spec replace_clauses(t(), atom(), non_neg_integer(), [clause()]) :: t()
Replaces the entire clause list for {name, arity} -- the primitive Episteme.Engine's retract/1/retractall/1 build on.
@spec sync(t()) :: :ok
Forces any buffered writes to durable storage. A no-op on the default in-memory backend.