Why Debugging Prolog Feels Different
Debugging an imperative program usually means stepping through a sequence of assignments to find the line that produced a wrong value. Debugging Prolog means reasoning about unification and backtracking instead: a bug is typically a wrong answer, a missing answer, or an infinite loop, and the cause is usually an unexpected clause ordering, an unintended variable binding, or a search path that never terminates rather than a single faulty statement.
Cricket analogy: It is like reviewing why a run-out appeal failed, which is not about 'which line of code executed' but about which unification of foot-to-crease timing did not hold, closer to checking replay angles than a checklist.
The Built-in Tracer: trace/0 and the 4 Ports
Calling trace/0 activates Prolog's box model debugger, which reports every goal invocation through four ports: Call when a goal is first invoked, Exit when it succeeds, Redo when backtracking re-enters it looking for another solution, and Fail when it fails outright. The leash/1 predicate controls which of these ports actually pause execution for interactive inspection, letting you skip noisy ports like Call while still stopping on Fail.
Cricket analogy: It is like a stump microphone paired with four camera angles, run-up, delivery, impact, and reaction, each capturing a distinct phase of the ball, with the third umpire able to pause on whichever phase matters.
% Buggy: intended to compute factorial
fact(0, 1).
fact(N, F) :- N > 0, N1 is N - 1, fact(N1, F1), F is N * F1.
?- trace, fact(3, X).
% Call: (8) fact(3, _1234) ?
% Call: (9) 3>0 ?
% Call: (9) _1240 is 3-1 ?
% ...
% X = 6.Spy Points and Selective Tracing
Tracing an entire program port by port is overwhelming once a program grows past a handful of predicates. spy/1 sets a breakpoint on a specific predicate indicator, such as spy(fact/2), so the tracer only activates when that predicate is called, and nospy/1 removes it again. This lets you isolate suspected predicates without wading through unrelated goals firing elsewhere in the program.
Cricket analogy: It is like placing a fielder specifically at deep midwicket to watch for one batter's favorite shot rather than reviewing footage of the entire innings ball by ball.
Once you have found the issue, call notrace/0 (or nodebug/0) to turn off the debugger quickly without restarting the query, and nospy/1 to clear any breakpoints you set with spy/1 so they don't keep firing on later runs.
Common Bug Patterns
Three patterns account for most Prolog bugs. A singleton variable, one that appears only once in a clause, is almost always a typo, and SWI-Prolog will warn about it at load time. A missing cut lets a predicate that should succeed once backtrack into unwanted alternative solutions on redo. And confusing is/2 with =/2 causes subtle failures: is/2 evaluates its right-hand side as arithmetic and requires it to already be a bound expression, while =/2 merely attempts unification and succeeds on structurally matching terms without evaluating anything.
Cricket analogy: It is like a scorer typo-ing a player's name once so it never matches their other entries, a singleton that never links back and breaks the season stats, alongside forgetting to declare an innings closed, a missing cut that keeps offering extra overs indefinitely.
Do not reach for cut (!) purely to suppress unwanted extra solutions without understanding why they appear. A cut placed to paper over a symptom can hide a genuine logic error in the rules, and it also makes the predicate less reusable, since it forces one deterministic reading even in contexts where exploring alternatives was legitimate.
- Prolog bugs are usually wrong answers, missing answers, or infinite loops rather than crashes.
- trace/0 activates the four-port box model debugger: Call, Exit, Redo, Fail.
- leash/1 controls which of the four ports pause execution for inspection.
- spy/1 and nospy/1 let you debug selectively by predicate instead of tracing the whole program.
- Singleton variable warnings often point to typos in variable names.
- A missing cut can cause a predicate to backtrack into unwanted extra solutions.
- Using cut purely to suppress unwanted backtracking can hide logic errors and hurt reusability.
Practice what you learned
1. What are the four 'ports' in Prolog's box model debugger?
2. Which predicate lets you set a breakpoint on a specific predicate instead of tracing the entire program?
3. What typically causes SWI-Prolog to emit a 'singleton variable' warning?
4. What is a common consequence of a missing cut in a Prolog predicate meant to return one answer?
5. Why is using cut solely to suppress unwanted backtracking considered risky?
Was this page helpful?
You May Also Like
Prolog for Expert Systems
Learn how Prolog's rule-based inference engine maps naturally onto expert system architecture, from knowledge bases to forward and backward chaining.
Modules in Prolog
Learn how Prolog's module system namespaces predicates, controls visibility, and enables code reuse across larger logic programs.
Prolog and AI Search Algorithms
See how classic AI search strategies, depth-first, breadth-first, and best-first, map onto Prolog's own backtracking engine and how to implement them explicitly.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics