Facts & Rules
prolog1-1 covered plain facts. This chapter adds rules — facts that depend on other goals being true — and gets precise about what actually organizes a Prolog program.
Rules — Facts That Depend on Other Facts
:- reads as "if." This rule says: grandparent(X, Z) is true if parent(X, Y) is true and parent(Y, Z) is true. The comma is logical AND — a genuinely different reading from every other language's own comma usage on this site.
The Body Is Just More Goals
A rule's body is itself a sequence of goals, each one potentially a plain fact or another rule. Rules can call other rules — genuinely comparable in spirit to function composition — but querying a rule works exactly the same way as querying a plain fact. The caller can't tell the difference from outside at all, a real, clean uniformity.
Predicates — The Real Unit of Organization
A predicate is a name plus arity (argument count) — parent/2. Every fact and rule sharing that same name and argument count together define one predicate. Genuinely comparable to how haskell1-4's own multiple function equations (sumList's base case and recursive case) together define one function — Prolog's version has this exact "multiple clauses, one definition" shape.
Recursive Rules — Building ancestor from parent
A real, genuinely recursive rule — base case plus recursive case, directly comparable to haskell1-4's own base-case/recursive-case shape for functions. The difference: this recursively defines a relation, not a function computing a single value.
Variables Are Scoped to a Single Clause
A real, easy-to-miss detail: a variable's name is scoped to a single clause only. Reusing X across two clauses of the same predicate does not link them — each clause gets its own completely independent set of variables, a real departure from ordinary lexical scoping in every other language covered so far.
Anonymous Variables — The Underscore
_ means "some value exists here, but I don't care what it is, and I won't bind it to a name." Directly comparable to haskell1-4's and haskell1-7's own discard pattern — a real, genuine syntactic and conceptual convergence between the two languages.
| Aspect | Haskell (haskell1-4/haskell1-8) | Prolog |
|---|---|---|
| Multiple definitions, one unit | multiple function equations | multiple clauses (facts/rules) of one predicate |
| Discard/don't-care binding | _ in a pattern | _ as an anonymous variable |
| What defines "the same unit" | function name | name + arity together (parent/2) |
parent/2 and a hypothetical parent/3 would be two completely separate, unrelated predicates in Prolog, despite sharing a name — get in the habit of naming predicates by their arity too when thinking or writing about them.
X in two different clauses of the same predicate looks like it might connect them — it doesn't. Each clause's variables are completely independent, a genuine, real source of early confusion coming from languages where a reused name usually means something.
Coding Challenges
Write parent/2 facts for a small three-generation family, then write a grandparent/2 rule using two parent/2 goals, and query it for a specific grandparent-grandchild pair.
📄 View solutionWrite a recursive ancestor/2 predicate (base case + recursive case) using your own parent/2 facts, and query it for an ancestor relationship that spans three generations, requiring the recursive case to fire.
📄 View solutionWrite a short comment explaining why writing X in two separate clauses of the same predicate does NOT create any connection between them, and what a programmer should do instead if they genuinely need to compare or relate two values across separate parts of a rule.
📄 View solutionChapter 2 Quick Reference
- :- reads as "if"; a comma in a rule body is logical AND
- A rule's body is just more goals — calling a rule looks identical to calling a plain fact from outside
- A predicate is a name+arity pair (parent/2) — every fact/rule sharing both together defines one predicate, the same "multiple clauses, one unit" shape as haskell1-4's own function equations
- Recursive rules (ancestor/2) use the same base-case/recursive-case shape as haskell1-4's own recursive functions, but define a relation rather than compute a value
- Variables are scoped to a single clause — the same name in two clauses is NOT connected
- _ is the anonymous variable — "something exists, I don't care what," directly comparable to haskell1-4's/haskell1-7's own discard pattern
- Next chapter: unification — Prolog's real central mechanism, contrasted directly with Haskell's own pattern matching