- an alphabet of constants (c), variables (v), function symbols (f), predicates (p), connectives, and qualifiers;
- a first order language;
- a set of axioms; and
- a set of inference rules.

terms: t ::= c | x | f( t[1] ... t[n] ) atomic formulae: a ::= p( t[1] ... t[n] ) well-formed formulae: wff ::= a | wff AND wff | wff OR wff | ~wff | wff -> wff | FORALL x . wff | EXISTS x . wffWe don't need all of these connectives and qualifiers:

EXISTS x F = ~ FORALL x . ~F A AND B = ~(~A OR ~B) A -> B = ~A OR BIn fact it is possible to transform any wff into

FORALL x[1] ... x[n] . L[1] OR L[2] OR ... L[m]where literals

`L[i]`

are `a | ~a`

and
`x[1] ... x[n]`

are all the variables of `L[1] ... L[m]`

(i.e. clauses have no free variables).
The Horn clauses are a subset of these where a clause has at most one non-negated literal. For example,

A OR ~B OR ~C or ~D (equivalent to) (B AND C AND D) -> A.We write the non-negated literal first. Since all Horn clauses enjoy this regular structure we write them as:

A <- B C D (or) A :- B C D

`A`

is the head of the clause and `B, C, D`

are its
sub-clauses (or subgoals). A Prolog program consists of:
- a set of facts (horn clauses with no non-negated literals);
- a set of rules relating various facts (horn clauses);
- a query (horn clause with no non-negated literal).

female(ann). female(mary). female(diane). parents(ann,fred,mary). parents(diane,fred,mary). parents(mary,john,liz). sister(X,Y) :- female(X), parents(X,M,F), parents(Y,M,F). ?sister(ann,diane). (true) ?sister(ann,mary). (false) ?sister(ann,X). (true, X = {ann,diane}) ?sister(X,diane). (true, X = {ann,diane})Executing a Prolog query is done in the following manner.

- Find first rule of database whose head has the same predicate as the head head of the goal (query).
- If a fact, recursively match corresponding formulae.
- If a rule, match corresponding formulae of head and goal, and when they all match, recursively attempt to satisfy each subgoal in RHS of rule.

`Match: term x term -> assignment`

match X, X empty set c, c empty set c1, c2 fail (c1 != c2) X, c1 [X |-> c1] c1, X [X |-> c1] X, Y [X |-> Y]Look familiar? Its unify! When we fail, we go back to step 1 and try the next rule. If there are no more rules, fail the most recent subgoal.

To implement a prolog interpreter we keep a stack of goals that records how to continue the search in step 1 and how to reset variables bindings as they were where the search must be resumed.

lhs: rule -> atomic rhs: rule -> list of atomic head: atomic -> predicate vars-of: (rule or atomic) -> set of vars (define stk (#f)) (define variables '()) (define search (lambda (goal) (letrec (loop ((lambda (db) (cond ((null? db) (fail)) ((eq? (head goal) (head (lhs (car db))))) (if (let/cc k (set! variables (filter var? variables)) (push! stk (cons k variables)) (let ((rule (instance (car db)))) (set! variables (append vars-of rule variables)) (unify goal (lhs rule)) (for-each search (rhs rule)) #t)) #t (loop (car db))) (else (loop (cdr db))))))) (loop db)))) (define fail (lambda () (let ((backtrack (pop! stk))) (set! variables (cdr backtrack)) (reset-variables!) ((car backtrack) #f)))) (define query (lambda (q) (set! stk (empty-stack)) (if (let/cc k (push! stk (cons k '())) (set! variables (vars-of q)) (search q) (display answer) #t) #t (printf "no solution"))))The list data type is represented in Prolog as

`[X | Y]`

. The first
element is called the head and the last is called the tail. We can define
member in the following way:
member(X,[X | _]). member(X,[_ | Y]) :- member(X,Y) ?member(ann,[ann,diane,mary]). (true)We can also define append:

append([],L,L). append([X,L1],L2,[X | L3]) :- append(L1,L2,L3). ?append([a,b],[c,d],X). (yes. X = [a, b, c, d]) ?append(X,[c, d], [a, b, c, d]). (yes. X = [a, b])

Prolog is NOT logic. One reason is that the order of rules in the database matters.

g(foo). p(X) :- p(X). p(X) :- q(X). ?p(Z).This yields an infinite loop. But swapping the two rules for

`p(X)`

it will terminate with the correct answer.
It is not always possible to arrange rules in such a way as to ensure termination. Hence Prolog adds a "cut" operator, denoted by the exclamation point. It controls search order. A clause of the form:

h(X) :- f(X), !, g(X)indicates that once f(X) is satisfied, the binding for X is fixed and can not be undone by backtracking. The cut operator cuts off backtracking through the proceeding subgoals. Cut has at best tenuous connections with logic.

- Complete the interpreter.
- Add cut.
- Verify that it corresponds to some existing Prolog interpreter.