API Reference Episteme v#0.1.0

Copy Markdown View Source

Modules

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.

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.

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.

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). Each succeeds exactly once, side effect aside.

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.

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

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.

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

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.

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

Episteme's term representation: atoms and numbers are plain Elixir atoms/integers/floats, 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.

A compound term name(args...).

A logic variable: ref is its identity, name is display-only.