Horn clauses & the Prolog interpreter
◈ 4 cardsWhy the Horn-clause restriction makes logic programming efficient, the three clause forms (fact / rule / goal), and how Prolog = resolution with left-to-right depth-first backtracking plus the cut.
From general resolution to something fast
General resolution is complete but exponential. Prolog buys tractability by restricting the language to Horn clauses, and this single restriction is the design decision that turns logic into a programming language.
What a Horn clause is
A Horn clause contains at most one positive literal. That restriction yields exactly three shapes, which map one-to-one onto Prolog:
- Fact — a single positive literal, no body: . In Prolog:
parent(tom, bob).An unconditional assertion. - Rule — one positive head, one or more body conditions: . In Prolog:
grandparent(X,Z) :- parent(X,Y), parent(Y,Z).Read declaratively: " is true if all of are." - Goal (headless clause) — no positive literal, a conjunction of goals to satisfy: . In Prolog:
?- grandparent(tom, ann).This is the negated query — exactly the negated goal you add in resolution refutation.
Not everything fits: has two positive literals and no Horn form. But most practical knowledge does, and the payoff is huge — Horn-clause databases support an efficient linear input form, unit preference, left-to-right, depth-first refutation that is refutation-complete (van Emden & Kowalski, 1976).
How the interpreter runs
Given a goal clause , the Prolog interpreter:
- Selects the leftmost goal (left-to-right).
- Scans the program top-to-bottom for the first clause whose head unifies with .
- On a match, replaces with that clause's body (its subgoals go to the front of the goal list). The goal list behaves as a stack, which is what makes the search depth-first.
- On failure, backtracks to the most recent choice point and tries the next matching clause.
So Prolog is resolution: unifying a goal with a clause head and replacing it with the body is one linear-input resolution step. Clause order and goal order are not logically meaningful in pure logic — but in Prolog they control the search, which is why "the same program in a different clause order" can loop forever or terminate.
Three things Prolog assumes (and the cut)
Prolog quietly enforces three axioms: the unique name axiom (distinct names = distinct individuals), the closed-world axiom (anything not provable is taken to be false — this is negation as failure, \+), and the domain closure axiom (the only individuals are those named in the database). The closed-world assumption is powerful but dangerous: unknown gets treated as false.
The cut (!) prunes backtracking — once crossed, it commits to the current clause choice and all bindings made before it. Cut makes programs faster and lets you express "don't reconsider," but it sacrifices the guarantee of finding all solutions (or even an alternative solution that exists down another path).