Negation as Failure
prolog1-8 ended with a brief preview: Course 2 would properly explain \+, Prolog's negation operator, commonly built internally from exactly the cut-based pattern that chapter showed. This chapter delivers on that promise — and, in keeping with this course's own honesty about assert/retract last chapter, is equally direct about what \+ genuinely does not guarantee.
What \+ Actually Means
Read this the way prolog1-8 taught you to read cut. If Goal succeeds, the first clause's body runs: the cut commits, then fail forces the whole thing to fail — so \+ Goal fails whenever Goal itself succeeds. If Goal fails, the first clause never gets past its own body, so Prolog falls through to the second clause, \+ Goal., which unconditionally succeeds. The net effect: \+ Goal succeeds exactly when Goal cannot be proven, and fails exactly when it can.
This is the classic, genuinely useful shape: "assume something is true unless it can specifically be shown otherwise." can_fly(pingu) fails because penguin(pingu) succeeds, so \+ penguin(pingu) fails — the negation working exactly as intended here.
The Closed-World Assumption
Notice what \+ Goal is actually testing: not "Goal is false," but "Goal cannot currently be proven from what's in the database." Prolog operates under the closed-world assumption — anything not provable from the known facts and rules is treated as false, full stop, with no third option for "unknown." That's a genuinely different, stronger claim than classical logical negation makes, and it's the real, well-documented limitation this chapter is named for.
This succeeds — but only because this particular flight/2 database happens not to list a Paris-Tokyo route, not because a Paris-Tokyo flight is provably impossible. Absence of evidence isn't evidence of absence: \+ can only ever report "not provable from what I currently know," and an incomplete database will report that honestly, misleadingly, exactly like a complete one would.
A Concrete Gotcha — Unbound Variables Inside a Negated Goal
The first query fails, because flight(paris, X) genuinely does succeed — for X = london, among others — so its negation fails. The second succeeds, because nothing matches flight(rome, X) at all in this database. The gotcha: with a free variable inside Goal, \+ Goal is really asking "does no value of X make this true," existentially quantified over every possible binding — not "is this specific unknown X not a flight destination." And whichever way it resolves, X itself comes back unbound either way — \+ never exposes any binding Goal might have found internally, even when it fails because of one.
| Claim being made | Handles an incomplete database? | |
|---|---|---|
| Classical logical negation | Goal is actually false | yes — true/false/unknown are distinct |
| Prolog's \+ (negation as failure) | Goal cannot currently be proven | no — "not provable" and "false" are collapsed into one |
\+ behaves most predictably when every variable inside Goal is already bound (ground) by the time it runs — the "generate, then test" idiom: find a candidate value first with an ordinary goal, and only then check \+ some_condition(Candidate) against it, rather than leaving free variables for \+ itself to reason about existentially.
\+ is only ever as trustworthy as the completeness of the facts and rules it's checking against. A genuinely missing flight route, a genuinely-not-yet-recorded fact, and a genuinely impossible one all look identical to \+ — this is the real, unavoidable cost of the closed-world assumption, not a bug to be fixed.
Coding Challenges
Write the bird/1, penguin/1, and can_fly/1 predicates exactly as shown in the chapter, add a new fact bird(ostrich) and penguin-style fact flightless(ostrich), update can_fly/1 to also check \+ flightless(X), and confirm can_fly(ostrich) correctly fails.
📄 View solutionUsing the flight/2 facts from the chapter, write a query using \+ that appears to confirm there is no flight from london to tokyo, then write a comment explaining why this result does not actually prove such a flight is impossible in reality.
📄 View solutionWrite a short comment explaining, using \+ flight(paris, X) as the concrete example, why X remains unbound after the query runs regardless of whether the query itself succeeds or fails.
📄 View solutionChapter 3 Quick Reference
- \+ Goal succeeds when Goal cannot be proven, fails when Goal succeeds — built internally from Goal, !, fail exactly as prolog1-8 previewed
- This is negation as failure, not classical logical negation — "not provable" is treated as "false," with no separate "unknown"
- The closed-world assumption: anything absent from the database is treated as false, even if it's simply missing rather than genuinely impossible
- \+ Goal with a free variable asks "does no value make Goal true" — existentially quantified, not testing one specific unknown value
- \+ never exposes any variable bindings from Goal, whether it succeeds or fails
- Ground a goal (bind its variables first) before negating it when possible, for predictable results