Capstone — Building a Small Project
The final chapter of the full Prolog track. This capstone builds a real N-Queens solver — combining prolog2-7's CLP(FD), prolog1-8's cut, prolog2-1's findall, and prolog2-5's DCG notation into one working project — and closes with the comparison this entire track was framed around from its very first chapter: two languages, both routinely called "declarative," solving computation in genuinely different ways.
The Problem — N-Queens
Place N queens on an N×N chessboard so that no two attack each other — no shared row, no shared column, no shared diagonal. A classic constraint-satisfaction problem, and exactly the shape prolog2-7 named as CLP(FD)'s natural strength.
Modeling With CLP(FD)
Queens is a list of N domain variables, one per column, each holding that column's queen's row — column position is implicit in the list index, so "no shared column" is automatic. ins is prolog2-7's own in, pluralized to apply one domain declaration to an entire list at once. safe_queens/1 walks the list, posting a #\= (row clash) and a diagonal-distance constraint between every pair of queens — real unification and real constraint propagation, narrowing the search before label/1 ever commits to a single value.
One Solution vs. All Solutions
The cut after queens(4, Qs) is prolog1-8's own backtracking-control tool, put to genuine use — it commits to the first solution label/1 finds rather than leaving choice points open for a caller who only wants one answer. findall — prolog2-1's own meta-predicate — does the opposite: it drives queens/2 to exhaustion, collecting every valid 6-queens board into one list. Four is the well-known correct count of distinct solutions for N = 6.
Rendering a Board With a DCG
prolog2-5's --> notation, reused to generate a row rather than parse one — DCGs run perfectly well in either direction. For each column C, symbol/2 emits q if that column's queen sits in the row currently being rendered, otherwise e.
The cut in symbol(Row, Row) --> !, [q]. is genuinely load-bearing, not just tidy — and it's a red cut by prolog1-8's own test. Without it, backtracking into symbol/2 at a queen's own column could still also match the catch-all second clause, producing an additional, factually wrong reading of that cell as empty. Removing this cut would genuinely change which results the predicate can produce — the defining test for red vs. green.
One last, quiet connection: forall/2 is standardly defined as forall(Cond, Action) :- \+ (Cond, \+ Action). — double negation, built directly from prolog2-3's own \+. This capstone never calls \+ by name, but it's running underneath every row printed, exactly the way prolog2-6 showed a meta-predicate can be a thin layer reusing something more fundamental.
Chapter Attribution
| Piece | Chapter |
|---|---|
| Domain variables, #\=, #=, labeling | prolog2-7 — CLP(FD) |
| Committing to the first solution | prolog1-8 — Cut |
| Collecting and counting all solutions | prolog2-1 — findall |
| The board-rendering grammar | prolog2-5 — DCGs |
| The red cut inside symbol/2 | prolog1-8 — green vs. red cuts, applied for real |
| forall/2's own internal definition | prolog2-3 — Negation as Failure (used indirectly) |
| Unification, backtracking throughout | Course 1 — prolog1-3, prolog1-4 |
prolog2-2's assert/retract has no natural role here — this solver needs no mutable state remembered across calls, since the whole problem is expressed and solved as one self-contained relation. prolog2-4's difference lists are used only invisibly, underneath the DCG translation, never written out by hand in this chapter. And prolog2-6's meta-interpreter — a teaching device for demonstrating homoiconicity — has no reason to reappear in a capstone that's solving a real problem rather than interpreting one; solve/1 was never meant to replace Prolog's own engine for everyday use.
Two Declarative Paradigms — Closing the Prolog Track
Every earlier chapter that named Haskell did so piece by piece — unification against pattern matching, backtracking against the [] monad, cut and assert against unsafePerformIO. This capstone is the place to say the whole comparison plainly, using N-Queens itself as the concrete anchor.
A Haskell N-Queens solver would be a genuine function — most naturally built over the [] monad's own do-notation (haskell2-3), generating candidate placements, filtering out attacking ones, and returning a concrete list of valid boards as its result. The search is real, but it's expressed as ordinary values flowing through ordinary function composition — solve(N) :: [[Int]] is a pure computation, evaluated to produce a value, the same as any other Haskell expression.
This chapter's queens/2 is not a function computing a value — it's a relation, and Queens is not returned so much as discovered: a set of variable bindings that happen to satisfy every constraint posted against them, found by an engine built to search exactly that way. CLP(FD) makes the search efficient, but it doesn't change what's actually happening underneath — Prolog was never evaluating an expression to a value; it was finding bindings that make a relation true.
| What "solving N-Queens" means | How nondeterminism is expressed | |
|---|---|---|
| Haskell | a pure function evaluated to a list of results | the [] monad — do-notation over ordinary values |
| Prolog | a relation, satisfied by a discovered set of bindings | backtracking search, native to the language itself |
Both are honestly "declarative" — neither language makes you write an explicit loop or a mutable counter to find these boards. But they earn that label in two structurally different ways, and this whole track, from prolog1-1's very first "no main" surprise to this capstone's own solver, has been the concrete demonstration of exactly what that difference looks like in practice.
Coding Challenges
Write queens/2 and safe_queens/1 and safe_queens/3 exactly as shown in the chapter, query queens(5, Qs), !, and confirm the result satisfies every constraint (no shared row, no shared diagonal) by hand.
📄 View solutionUse findall/3 to collect every solution to queens(5, Qs), report the total count, and confirm it matches the well-known correct value of 10 distinct 5-queens solutions.
📄 View solutionWrite a short comment explaining, with a concrete backtracking query into row_symbols/3, what extra (incorrect) result the cut inside symbol/2 is specifically preventing, and why this makes it a red cut rather than a green one by the chapter's own test.
📄 View solutionChapter 8 Quick Reference — Prolog Intermediate/Advanced Complete
- queens/2 models N-Queens as domain variables, #\= and diagonal-distance constraints, and label/1 — prolog2-7's CLP(FD) put to real use
- Cut after queens(N, Qs) commits to one solution; findall/3 collects and counts every solution instead
- A DCG can generate output, not just parse input — row_symbols/3 reuses prolog2-5's --> notation to render a board
- symbol/2's cut is a genuine red cut — removing it would let an incorrect extra "empty" reading of a queen's own cell through
- forall/2 is itself built from double negation (\+), a quiet reappearance of prolog2-3 underneath a capstone that never calls \+ directly
- Haskell solves N-Queens as a pure function evaluated to a value; Prolog solves it as a relation, satisfied by bindings a search engine discovers — the track's own throughline, made concrete one final time
- Prolog Intermediate/Advanced is now complete. The full Prolog track — 16 chapters across 2 courses — is done.