# Episteme v0.2.0 - Table of Contents

> A standalone Prolog-like resolution engine and clause database for Elixir: terms, unification, SLD-resolution, backtracking, a genuine clause-scoped cut, and builtin predicates, built on Ichor's search substrate. No parser -- bring your own front-end (e.g. Aletheia) or build goal terms directly.

## Pages

- [Episteme](readme.md)
- [Tutorial](tutorial.md)
- [Reference](reference.md)
- [Examples](examples.md)
- [Case study: auditing structured application logs](case_study.md)
- [Cheatsheet](cheatsheet.md)
- [Changelog](changelog.md)
- [Contributing](contributing.md)
- [LICENSE](license.md)

## Modules

- [Episteme.Builtins.Strings](Episteme.Builtins.Strings.md): The atom/number/string conversion family: `atom_codes/2 atom_chars/2
atom_length/2 atom_concat/3 char_code/2 number_codes/2
number_chars/2 upcase_atom/2 downcase_atom/2 atomic_list_concat/2,3`
(classic ISO/de-facto predicates, working over atoms) plus
`atom_string/2 string_to_atom/2 string_concat/3 string_chars/2
string_codes/2 string_length/2 number_string/2 split_string/4`
(SWI-style predicates producing/consuming real strings), all built
on the shared
`atomic_text/1`/`require_atomic_text/2` "coerce an atomic term (atom,
number, or real string) to its text" primitive. `string/1`'s own type
check lives in `Episteme.Engine` directly, alongside every other type
check (`atom/1`, `var/1`, ...), not here.
- [Episteme.Dcg](Episteme.Dcg.md): Definite Clause Grammar support: translates a DCG rule/body (`-->`,
the "difference list" grammar-rule notation) into an ordinary
Episteme clause/goal, threading two extra arguments -- the list
before (`S0`) and after (`S`) whatever the rule/body consumes --
through every nonterminal call. `phrase/2,3` and `dcg_translate/2`
(`Episteme.Engine`), a `{:dcg, head, body}` `Episteme.Database.consult_forms/2`
form, and DCG-rule support in `assert/1` (a `-->/2`-shaped clause
term, same as `:-/2` is already special-cased) are all built on
`translate_body/3` below.

- Top-level API
  - [Episteme](Episteme.md): Top-level API for querying an already-built `Episteme.Database`:
`query/2` solves a goal term against one, returning every solution's
variable bindings. No parser lives here -- a goal is always an
already-built term (`Episteme.Term.new_var/1`, `%Episteme.Term.Compound{}`,
plain Elixir atoms/numbers/lists), and a database is built directly via
`Episteme.Database.add_clause/2`/`add_fact/2`, or via `consult_forms/2`
for a front-end that parses its own surface syntax into
`{:fact, _}`/`{:rule, _, _}` forms. Aletheia's reader is one such
front-end, built on top of this library; this library has no
dependency on it or on any particular concrete syntax.

- Term &amp; Database
  - [Episteme.Database](Episteme.Database.md): 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`/
`abolish`/`dynamic` 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, _, _}`/`{:dcg, _, _}` forms (Aletheia's reader
is one such front-end, but this module has no dependency on it or
any particular concrete syntax). A `{:dcg, head, body}` form is a
DCG rule (`Head --> Body`) -- translated via `Episteme.Dcg.translate_rule/2`
into an ordinary clause before storage, same as `assert/1`
(`Episteme.Engine`) does for a `-->/2`-shaped clause term asserted
directly.
  - [Episteme.Term](Episteme.Term.md): Episteme's term representation: atoms and numbers are plain Elixir
atoms/integers/floats, a real Prolog string is a plain Elixir binary
-- a genuinely new atomic term
class, not a list of codes, but still no dedicated struct, the same
"raw host-language value" pattern atoms/integers/floats already use --
lists are native Elixir lists, variables and compounds get dedicated
structs. This module is the
`Ichor.Backtrack.Term` implementation passed explicitly to every
`Ichor.Backtrack.Bindings` call, plus the handful of whole-term
operations (deep resolve, rename-apart) built on
`Ichor.Toolkit.TermWalk` that unification itself doesn't need but the
engine does.

  - [Episteme.Term.Compound](Episteme.Term.Compound.md): A compound term `name(args...)`.
  - [Episteme.Term.Var](Episteme.Term.Var.md): A logic variable: `ref` is its identity, `name` is display-only.

- Database Backends
  - [Episteme.Database.Backend](Episteme.Database.Backend.md): The storage contract `Episteme.Database` delegates every clause-storage
operation to, so the database's actual persistence strategy is a
pluggable choice rather than baked into `Episteme.Database` itself.
Ships two implementations -- `Episteme.Database.Backends.Ets` (the
default: an in-memory ETS table, indexed by `{name, arity}`) and
`Episteme.Database.Backends.Dets` (the same shape, backed by a DETS
file on disk, for a database that should survive past the owning
process) -- and any third module implementing this behaviour works
too: pass it as `Episteme.Database.new/1`'s `:backend` option.
  - [Episteme.Database.Backends.Dets](Episteme.Database.Backends.Dets.md): An on-disk `Episteme.Database.Backend`, for a database that should
survive past the owning process (or the VM) -- same `{name, arity}`-keyed
`:set` shape as `Episteme.Database.Backends.Ets`, backed by `:dets`
instead. Requires a `:file` option (a binary or charlist path) on
`Episteme.Database.new/1`; `:table` names the DETS table itself
(defaults to a name derived from `:file`, so two databases opened
against different files don't collide).
  - [Episteme.Database.Backends.Ets](Episteme.Database.Backends.Ets.md): The default `Episteme.Database.Backend`: an in-memory `:ets` `:set`
table keyed by `{name, arity}`, each row's value the predicate's own
clause list -- so clause order (what backtracking order depends on) is
exactly that Elixir list's order, never ETS's own unspecified
multi-row traversal order. `:public` access, so any process holding
the same `Episteme.Database.t()` value can read and mutate it, not
just the process that called `init/1` -- there is no coordination
beyond what `:ets` itself serializes, so concurrent `assert`/`retract`
against the *same predicate* from multiple processes can race (a
read-modify-write on one row); concurrent access to *different*
predicates never conflicts.

- Engine
  - [Episteme.Engine](Episteme.Engine.md): SLD-resolution over `Ichor.Backtrack.Tree` + `Ichor.Backtrack.Bindings`
plus `Episteme.Database` -- clause selection is `disjunction/2` folded
over a predicate's clauses in declared order, subgoal sequencing is
`conjunction/2`, both reused unmodified from Ichor. Control constructs
(`and`, `or`, `if_then`/`if_then_else`, `cut`, `not/1`, `call/N`,
`once/1`, `ignore/1`, `phrase/2,3`) live directly here rather than in a separate builtins
module: they turned out, once actually implemented, to be inseparable
from the cut-barrier machinery below, not independent predicates
dispatchable through a generic table. Every one of these is a plain
English name, not the traditional Prolog operator (`,`/`;`/`->`/`!`/
`\+`) it corresponds to -- Episteme has no reader of its own (see
`Episteme`'s moduledoc), so there's no textual syntax for any of this
to look "conventional" against; a word you can actually remember beat
matching ISO Prolog's punctuation.

- Builtins
  - [Episteme.Builtins.Arithmetic](Episteme.Builtins.Arithmetic.md): Evaluates a Prolog arithmetic expression term to an Elixir number
under `bindings` -- `is/2`'s own right-hand side, and both sides of
every arithmetic comparison (`numeric_equal/2`, `</2`, ...). Raises (via
`Episteme.Builtins.Exceptions.prolog_throw/1`) a standard `type_error`
for anything that isn't a number, a bound variable, or a recognized
evaluable functor.
  - [Episteme.Builtins.Exceptions](Episteme.Builtins.Exceptions.md): Standard ISO error-term constructors (`type_error/2`, `domain_error/2`,
`instantiation_error/1`) plus the raw Elixir `throw` tag Episteme's own
`throw/1`/`catch/3` (in `Episteme.Engine`, which needs to `solve` the
recovery goal) and every builtin that detects bad input raise through.
  - [Episteme.Builtins.Io](Episteme.Builtins.Io.md): Minimal I/O: `write/1 writeln/1 print/1 nl/0` -- canonical
`functor(args)` text via `Episteme.Term.to_text/1` (no operator-aware
pretty-printing yet) -- plus `format/1,2`, a directive-based
formatter over the same `to_text/1` rendering. Each succeeds exactly
once, side effect aside.
  - [Episteme.Builtins.Lists](Episteme.Builtins.Lists.md): The list predicate family -- `length/2 append/3 member/2 reverse/2
nth0/3 nth1/3 last/2` -- over native Elixir lists (Episteme lists are
plain `[h|t]`, not cons compounds). Hand-rolled-goal style,
generalized to real unification via `Ichor.Backtrack.Bindings`.

