Anatomy of a Prolog file
Prolog is a declarative logic programming language: source files describe facts and rules, and the runtime searches for substitutions that make a query true.
File extensions: .pl, .pro
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Prolog file
Comment
Uses % for the rest of a line or /* ... */ for a block.
A percent sign starts a line comment, while C-style delimiters enclose a block comment. SWI-Prolog also recognizes structured comments used by PlDoc to generate predicate documentation. Comments do not participate in logical inference, but explaining the intended meaning of a relation is valuable because the same predicate can often be used in several directions.
Module directive
Names a module and lists the predicates it exports.
:- module(family, [ancestor/2]). is a directive evaluated while the file is loaded. ancestor/2 is a predicate indicator: the name followed by its arity, or number of arguments. Modules give predicates a namespace and make the public interface explicit. Directives resemble rules with no head because they instruct the Prolog system rather than add an ordinary relation to the knowledge base.
Fact
States an unconditional relation and ends with a period.
parent(ada, byron). is a clause whose body is implicitly true. Lowercase words such as parent and ada are atoms; the complete compound term asserts that the relation holds. Facts are not assignments or rows in a fixed schema. They are clauses that the inference engine can match through unification when a query asks about the same predicate.
Rule (`:-`)
Defines when a relation is true: the head holds if the body succeeds.
ancestor(X, Y) :- parent(X, Y). reads as “X is an ancestor of Y if X is a parent of Y.” The term before :- is the head; the goals after it form the body. A predicate can have several clauses. Prolog tries matching clauses in source order and can backtrack to later clauses when a choice fails or the caller requests another solution.
Variables and unification
Uppercase names are variables that become bound when terms unify.
X, Y, and Middle begin as logical variables. Unification finds consistent bindings that make two terms identical; it is more general than one-way assignment. An underscore starts an anonymous or intentionally ignored variable. Repeating a named variable within a clause requires every occurrence to receive the same value, which expresses data flow without a separate assignment statement.
Conjunction
A comma means logical “and”: each goal must succeed from left to right.
In parent(X, Middle), ancestor(Middle, Y), Prolog first finds a binding that satisfies parent/2, then tries the recursive goal with that binding. If the second goal fails, it backtracks into the first for another candidate. A semicolon expresses disjunction (“or”). Parentheses are worth using when both operators appear, because comma binds more tightly than semicolon.
Recursive relation
Defines a transitive relationship by referring to the same predicate.
The recursive ancestor/2 clause walks from a parent to a more distant descendant through Middle. The earlier base clause handles the direct-parent case and stops the recursion. Clause and goal order matter operationally even though the program is declarative. Putting the recursive call before the progress-making parent/2 goal could search an unbounded space before discovering useful bindings.
List
Uses [A, B] for elements and [Head|Tail] to split a list.
Lists are ordinary recursive terms. [] is the empty list, [a, b] is syntax for a chain of list cells, and [Head|Tail] unifies with a nonempty list while exposing its first element and remaining list. Many Prolog programs express iteration as recursion over this head-tail structure, though library predicates such as maplist/2 and findall/3 often make the intent clearer.
Goal and query
Calling a predicate asks Prolog to prove the goal and find variable bindings.
findall(Person, ancestor(ada, Person), People) runs the ancestor/2 goal for every solution and collects the resulting Person bindings into People. An interactive query uses the same goals after the ?- prompt, but the prompt itself is not normally stored in a source file. A goal can succeed once, succeed in multiple ways, or fail. Success does not return a Boolean object; it produces a proof state and any variable substitutions discovered along the way.
Initialization directive
Runs a goal when the file is used as a program.
:- initialization(main, main). is SWI-Prolog’s executable-entry convention. It calls main/0 only when the file is launched as the top-level script, not merely imported as a library. The ISO-compatible one-argument form schedules a goal after loading, while this two-argument form cleanly distinguishes command-line execution from consultation.
Official Prolog site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.