Cheatsheet: the Aletheia language

Copy Markdown View Source

Quick lookup once you already know Prolog and just need the Aletheia syntax/predicate specifics — every predicate and syntax feature, one page (ALETHEIA.md is the full page behind each row of it). For explanations, see the language tutorial. For using Aletheia as a library from Elixir (consult/query/the REPL), see ../CHEATSHEET.md instead.

Syntax at a glance

foo                  % atom
'John Doe'           % quoted atom
"hello"               % string (real Elixir binary, not a code list)
X, _Acc, _           % variables (bare _ is always fresh)
42, 3.14             % integer, float
[], [1,2,3], [H|T]   % lists
foo(a, b)            % compound
{Goal}                % '{}'/1 -- ordinary syntax, special meaning in a DCG body
1 + 2                % sugar for +(1, 2)
% comment            % line comment
/* comment */         % block comment
fact.                 % a fact
head :- body.         % a rule
head --> body.        % a DCG rule
:- goal.              % a directive (only op/3 does anything)

Default operator table

Bigger priority number = looser binding (ISO convention):

1100  xfy   ;
1050  xfy   ->
1000  xfy   ,
 900  fy    \+
 700  xfx   = \= == \== is =:= =\= < > =< >= @< @> @=< @>=
 500  yfx   + -
 400  yfx   * / // mod rem
 200  xfy   ^
 200  yfx   **
 200  fy    - +   (prefix)

--> isn't in this table -- a DCG rule is its own dedicated clause form, not parsed through the operator table.

Extend with :- op(Priority, Type, Name). — takes effect for clauses parsed after it in the same source.

Control

A, B                  % conjunction
A ; B                 % disjunction
Cond -> Then          % if-then (fails if Cond fails, no else)
Cond -> Then ; Else   % if-then-else
!                     % cut -- see below
\+ Goal               % negation as failure
call(Goal, Extra...)  % call/1..8, cut-opaque
once(Goal)            % first solution only, cut-opaque -- NOT the same as !
ignore(Goal)          % like once/1, but never fails
forall(Cond, Action)  % every solution of Cond has a solution of Action
true. fail. false.

Cut, in one example

p(1) :- !, fail.
p(2).
Aletheia.query("p(X)", db)  #=> {:ok, []}

Real cut, not once/1: commits to clause 1 before fail runs, so clause 2 never gets a chance. See ALETHEIA.md#cut for the full rules.

Unification / comparison

X = Term        % unify
X \= Term       % fails if it WOULD unify
A == B          % structural equal (int/float never equal)
A \== B
unify_with_occurs_check(X, Term)   % like =, but rejects X = f(...,X,...)
A @< B  A @> B  A @=< B  A @>= B   % standard order of terms
compare(Order, A, B)               % Order = <, =, or >

Type checks

var(X)  nonvar(X)  atom(X)  string(X)  atomic(X)  number(X)
integer(X)  float(X)  compound(X)  callable(X)  is_list(X)  ground(X)

Arithmetic

X is 2 + 3 * 4        % standard precedence, is/2 evaluates the RHS
+ - * / // mod rem ** ^ abs/1 sign/1 min/2 max/2 sqrt/1
sin/1 cos/1 tan/1 exp/1 log/1
=:= =\= < > =< >=      % arithmetic comparison (evaluates both sides)
between(Low, High, X)  % X unbound: enumerates; X bound: membership check

Exceptions

throw(Term)
catch(Goal, Catcher, Recovery)
type_error(Type, Culprit)
domain_error(Domain, Culprit)
instantiation_error(_)

Uncaught -> {:error, term} from Aletheia.query/2, not a crash. Undefined predicate -> existence_error(procedure, Name/Arity).

Database and aggregation

assert(Clause)  assertz(Clause)  asserta(Clause)  % assert is a classic alias of assertz
retract(Clause)  retractall(HeadPattern)
dynamic(Name/Arity)  abolish(Name/Arity)  clause(Head, Body)
findall(Template, Goal, List)   % [] when Goal has no solutions, not failure
bagof(Template, Goal, List)     % fails (not List = []) on no solutions; groups by free vars
setof(Template, Goal, List)     % like bagof/3, sorted + deduped

Lists

length(List, N)
append(A, B, C)          % also works with A unbound (splits C)
member(X, List)
reverse(List, Reversed)
nth0(I, List, X)  nth1(I, List, X)
last(List, X)
msort(List, Sorted)  sort(List, Sorted)   % msort keeps dups, sort dedups
permutation(List, Perm)
sum_list(List, Sum)  max_list(List, Max)  min_list(List, Min)
list_to_set(List, Set)
include(Goal, List, Kept)  exclude(Goal, List, Kept)
foldl(Goal, List, V0, V)   % also /5, /6 for 2-3 lists in lockstep
maplist(Goal, List1, ..., ListN)

Atom/number/string conversions

atom_*/number_* work with atoms (atom_chars/2's chars are single-char atoms); string_*/split_string work with real strings (string_chars/2's chars are single-char strings).

atom_codes(A, Codes)  atom_chars(A, Chars)  atom_length(A, Len)
atom_concat(A1, A2, A3)   % A3 only bound: enumerates every split
sub_atom(Atom, Before, Len, After, Sub)   % Sub always an atom
char_code(Char, Code)
number_codes(N, Codes)  number_chars(N, Chars)
upcase_atom(A, U)  downcase_atom(A, D)
atomic_list_concat(List, A)  atomic_list_concat(List, Sep, A)
atom_string(A, S)  string_to_atom(S, A)
string_concat(S1, S2, S3)  string_chars(S, Chars)  string_codes(S, Codes)
string_length(S, Len)  number_string(N, S)
split_string(S, SepChars, PadChars, SubStrings)

I/O

write(Term)  writeln(Term)  print(Term)  nl
format(Format, Args)   % also format/1, Args = []

format/2 directives: ~w/~p/~q (term text), ~a (atom text), ~d (integer text), ~s (code-list/string text), ~i (skip an argument), ~n (newline), ~~ (literal ~).

DCG

greeting --> [hello], [world].
digit(D) --> [D], { integer(D) }.
phrase(Body, List)          % Body must consume all of List
phrase(Body, List, Rest)    % leaves what's unconsumed in Rest
dcg_translate(Rule, Clause) % the readable alias for -->/2 itself