Purity, Monotonicity & Declarative Style

A pure Prolog program uses only unification, conjunction, disjunction, and clause selection (plus pure constraints like dif/2 and CLP).

27.1 The pure core

A pure Prolog program uses only unification, conjunction, disjunction, and clause selection (plus pure constraints like dif/2 and CLP). Pure programs are:

Monotonic: adding a clause can only add solutions; generalizing a query never removes solutions.

Relational: work in all directions (append/3, length/2).

Declaratively debuggable: you can reason about meaning by removing goals ("generalization") or clauses.

Impure features (!, \+, var/1, assert, is/2, side effects) are sometimes necessary but break these properties.

27.2 Examples of impurity problems

?- X = 1, \+ X = 2.     % true
?- \+ X = 2, X = 1.     % false — order matters (non-monotonic)

?- dif(X, 2), X = 1.    % true
?- X = 1, dif(X, 2).    % true — dif/2 is order-independent

27.3 Pure alternatives

\=, \==, \+ X = Y CLP(FD) #=, #< (C -> T; E) if_/3 with reified predicates (library(reif)) memberd_t/3, member/2 + dif (no pure equivalent; use carefully) clause indexing via distinct functors, if_/3

:- use_module(library(reif)).    % pack in SWI; built into Scryer
list_without([], _, []).
list_without([X|Xs], E, Ys0) :-
    if_(X = E, Ys0 = Ys, Ys0 = [X|Ys]),
    list_without(Xs, E, Ys).

27.4 Writing general predicates

Name predicates as relations: list_length/2 rather than get_length/2.

Test with the most general query: ?- list_length(L, N).

Ensure termination conditions: ?- Goal, false. should terminate (universal termination).

Use length(L, _) to fairly enumerate list lengths.

27.5 Declarative debugging: failure slices

Insert false into a clause body to isolate non-termination: if a "failure slice" (program with goals replaced by false) doesn't terminate, neither does the full program — you must fix the remaining visible part.