Episteme.Database (Episteme v0.1.0)

Copy Markdown View Source

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

clause()

@type clause() :: {head :: term(), body :: term()}

indicator()

@type indicator() :: {atom(), non_neg_integer()}

t()

@type t() :: %Episteme.Database{
  backend: module(),
  state: Episteme.Database.Backend.state()
}

Functions

add_clause(db, clause)

@spec add_clause(t(), clause()) :: t()

add_clause_first(db, clause)

@spec add_clause_first(t(), clause()) :: t()

Like add_clause/2, but prepends -- the primitive asserta/1 builds on.

add_fact(db, head)

@spec add_fact(t(), term()) :: t()

clauses_for(db, name, arity)

@spec clauses_for(t(), atom(), non_neg_integer()) :: [clause()]

close(database)

@spec close(t()) :: :ok

Releases whatever resources the backend holds (an ETS table, an open DETS file, ...).

consult_forms(db, forms)

@spec consult_forms(t(), list()) :: t()

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.

defined?(db, name, arity)

@spec defined?(t(), atom(), non_neg_integer()) :: boolean()

indicator(atom)

@spec indicator(term()) :: indicator()

new(opts \\ [])

@spec new(keyword()) :: t()

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).

replace_clauses(db, name, arity, clauses)

@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.

sync(database)

@spec sync(t()) :: :ok

Forces any buffered writes to durable storage. A no-op on the default in-memory backend.