Prolog Primer
Introduction
“Prolog is a logic programming language associated with artificial intelligence and computational linguistics. Prolog has its roots in first-order logic, a formal logic, and unlike many other programming languages, Prolog is intended primarily as a declarative programming language: the program logic is expressed in terms of relations, represented as facts and rules. A computation is initiated by running a query over these relations.”
—Wikipedia
“A Prolog program is a set of specifications in the first-order predicate calculus describing the objects and relations in a problem domain. The set of specifications is referred to as the database for that problem. The Prolog interpreter responds to questions about this set of specifications. Queries to the database are patterns in the same logical syntax as the database entries. The Prolog interpreter uses pattern-directed search to find whether these queries logically follow from the contents of the database.”
—George Luger, Artificial Intelligence: Structures and Strategies for Complex Problem Solving, 6th ed., 2009
Prolog is different from other languages you’ve seen. It is declarative, rather than imperative. We write rules and facts, and then query the system. We do not write functions or algorithms the way we do in imperative languages.
Prolog takes some getting used to, however, the structure of the language itself is quite simple.
Different authors sometimes use different vocabulary when describing the language elements of Prolog. For example, one author might speak of “predicates” and “arguments,” where a different author might refer to the same things as “relations” between “objects.” Here we will try to adhere to the ISO standard terminology.
Installation
macOS
On macOS, I strongly recommend installation via Homebrew.
brew install swi-prologIf you don’t have Homebrew, you probably should. See: the Homebrew website.
Linux
On Linux, use your package manager (APT, DNF, Pacman, Zypper, APK, etc.).
Microsoft Windows
For Windows, download pre-built binaries from the SWI-Prolog website.
Terms
Everything in Prolog is constructed from terms. A term is either an atomic term (which includes atoms and numeric literals), a variable, or a compound term (called by some authors a structure).
<atomic term> ::= <atom> | <number>
<term> ::= <variable> | <atomic term> | <compound term>
<term list> ::= <term> | <term> <term list>
<compound term> ::= <functor name>(<term list>)
We say a term is ground if it contains no variables.
The Prolog command-line interface (CLI)
We’re using SWI-Prolog (a widely-used dialect of Prolog). SWI-Prolog provides a convenient interface you can run in your browser, called “Swish”. Depending on how you installed SWI-Prolog, you may have a GUI front-end, or a CLI front-end, or both. I’ll give examples using the command-line interface (CLI).
We can invoke the SWI-Prolog CLI with swipl.
$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 10.0.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
1 ?-Now what? Notice the last line is 1 ?-. This is the SWI-Prolog prompt. We can interact with Prolog by issuing commands or queries at the prompt. The leading 1 here is a query number (and can be ignored). SWI-Prolog will increment this automatically.
Files and consulting files
We usually write our Prolog code—in the form of facts and rules—to a file, and then we consult the file to load it into working memory. Like most any other code, Prolog code is stored in plain-text format.
Using the plain-text editor or IDE of your choice, copy and paste the following into a new, empty file, and save the file as “greeting.pl”. By convention, Prolog uses the file extension .pl. (Your operating system may associate this file extension with Perl scripts by default, but you can change the file association if this becomes inconvenient.)
%% A simple collection of facts
greeting(arabic, 'السلام عليكم').
greeting(chinese, '你好').
greeting(english, 'Hello').
greeting(french, 'Bonjour').
greeting(german, 'Guten tag').
greeting(hindi, 'नमस्ते').
greeting(japanese, 'こんにちは。').Once the file is saved as “greeting.pl”, start the SWI-Prolog CLI, and at the prompt enter the filename within square brackets, and without the .pl extension. Be sure to follow with a full-stop .. SWI-Prolog will look in the current working directory for a file named “greeting.pl” and if found it will load it into memory.
?- [greeting].
true.If all goes well, SWI-Prolog will report true indicating that the file was found, and loaded into memory without error. To test, issue a simple query:
?- greeting(english, X).
X = 'Hello'.We’ll go over how this works in subsequent sections. For now, if you get these results, you’re all set.
If you’re in the process of editing file(s) that have already been consulted, you can re-consult all consulted files with make.
Paths and working directory
If you need to navigate to a particular working directory where you’ve saved your Prolog file, you can do this before or after invoking swipl.
Changing working directory before invoking swipl.
$ cd /path/to/your/working/directory
$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 10.0.2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
1 ?-Changing working directory after invoking swipl
?- cd('/path/to/your/working/directory').
true.Quitting SWI-Prolog
To quit, use halt..
?- halt.Atoms and variables
Prolog is case-sensitive, but not in the way you might think. Lower-case denotes an atom or the name of a functor. Variables start with upper-case letters.
hot(sun). % hot is the name of the functor. sun is an atom.
hot(X). % hot is, again, the name of the functor. X is a variableLike Python and many other languages (Rust, Go, OCaml, Haskell, Erlang, Lua, etc.), we can use the single underscore (_) as an identifier for a variable whose value we don’t need, don’t care about, and won’t read. Unlike other languages, where _ is used as an anonymous variable by convention, in Prolog, _ is the anonymous variable.
Clauses
A Prolog program consists of one or more clauses (typically more than one). Clauses in a program come in two flavors: facts and rules. Rules have a head and a body. Facts are clauses without a body.
The general form of a clause is
head :- body.We may read this as: if body is true, then the head is true. If the head is always true, without condition, we call this a fact, and we may omit the body.
The clause is the only language construct in Prolog. That’s it.
In BNF:
<clause> ::= <compound term> :- <compound terms> | <compound term>
<compound terms> ::= <compound term> | <compound term>, <compound terms>
Facts
Perhaps the simplest thing we can do in Prolog is to write facts about objects.
color(sky, blue).A fact is a clause without a body—there is no condition to be satisfied. It is a bare assertion (just what we want for a fact!). Facts must end with a period.
Note that the order of objects is arbitrary, that is, we could just as easily have written
color(blue, sky).However, these are two different facts!
It is usually helpful to have a reading of each type of fact, to help us remain consistent in usage. We might read color(sky, blue) as “the color of the sky is blue,” making clear that color(blue, sky) means something different.
Another example:
enjoys(mary, brussels_sprouts).This asserts that Mary enjoys brussels sprouts. The order of arguments is part of the meaning—we read the functor name and arguments consistently.
We refer to the part outside the parentheses as the functor name, the objects within as the arguments, and the number of arguments as the arity.
Some other facts (note that % indicates a single-line comment):
hot(sun). % the sun is hot
play(magnus, yifan, chess). % magnus and yifan play chess
gives(sarah, waldo, flower). % sarah gives a flower to waldoRules
Rules in Prolog consist of a head and a body, separated by the symbol :-. We read the :- as “if,” but it is the head which is true as a consequence of the body being true.
pumpkin(X) :-
winter_squash(X),
big(X),
orange(X).Remember that if we use the comma between goals, it means logical AND, so the reading we give this in Prolog is
X is a pumpkin IF
X is a winter squash
AND X is big
AND X is orange
This reverses the order we’re used to in imperative languages, which have an IF-THEN structure. This is roughly equivalent to
IF
X is a winter squash
AND X is big
AND X is orange
THEN
X is a pumpkin
We refer to the parts of the body as goals. The comma between goals represents the logical connective AND. Rules, like facts, end with a period.
So why are Prolog rules structured the way they are? The answer is rooted in the use of Horn clauses (we’ll get to these shortly).
Ordering
In Prolog, it’s best practice to keep your predicates alphabetized in the knowledge-base, and to group all predicates with the same identifier and arity together. This significantly speeds up debugging and makes large databases easier to traverse.
Connectives
In Prolog, the comma and semicolon have very different meanings than they do in other programming languages. We’ve seen that we use the comma to separate arguments. However, when building compound rules or queries—rules or queries that have more than one goal—the comma functions as logical AND. The semicolon functions as logical OR. Negation is written \+ (go figure).
X, Y % X AND Y
X; Y % X OR Y
\+X % NOT XHorn clauses
In logic, a clause is a disjunction of literals,
L_1 \lor L_2 \lor \ldots \lor L_n
where each L_k is a literal.
A Horn clause is a disjunction of literals with at most one positive literal. A definite Horn clause is a Horn clause with exactly one positive literal. A positive literal is just an atom, e.g., x, whereas a negative literal is the negation of an atom, e.g., \neg x.
The general form of a definite Horn clause is
H \lor \neg G_1 \lor \neg G_2 \lor \ldots \lor \neg G_n
In this form, we call H the head of the clause and the rest is the body of the clause.
Using De Morgan’s laws we may rewrite this as
H \lor \neg (G_1 \land G_2 \land \ldots \land G_n)
Recall that A \lor \neg B \equiv A \leftarrow B.
This lets us rewrite the general form as
H \leftarrow (G_1 \land G_2 \land \ldots \land G_n)
In Prolog, :- signifies \leftarrow and , signifies \land. So we may express this in Prolog as a rule:
H :- G1, G2, ..., Gn.To complete the picture, we need only consider facts and queries. A fact is just a head without a body—a bare assertion. A query is the opposite—a body without a head!
H :- G1, G2, ..., Gn. % a rule
H. % a fact
?- G1, G2, ..., Gn. % a queryIt turns out that all computations can be expressed as Horn clauses. This means that Prolog, which is constructed exclusively from Horn clauses, is Turing complete!
Variables and scope
As noted, variables are written with names starting with a capital letter, e.g., X, Weight, or Volume. Variables are declared by using them—unlike C or Java there is no explicit declaration.
The scope of a variable is limited to the rule or clause in which it appears. Variables have no life outside this scope.
Example (where ?- is the Prolog query prompt):
?- X = 2, write(X).
2
X = 2.This instantiates X with a value of 2 and prints it to the console. When the clause terminates (at the period), X goes out of scope.
If we continue:
?- write(X). % write is a Prolog built-in
_31860
true.This is a new X, which has no value. In Prolog we refer to this as an uninstantiated variable.
Note that the scope of variables is different from the scope of terms. Terms are always in scope.
When we write a rule, we use variables, for example:
is_parent_of(X, Y) :-
child_of(Y, X).Queries
We don’t run a Prolog program in the conventional sense. Instead we consult a file (or files) containing facts and rules, and then we issue queries.
Let’s say we had these rules:
pet(X) :-
mammal(X), % yeah, we're ignoring parakeets and turtles
\+human(X), % humans can't be pets
friendly(X), % otherwise, why bother?
\+prickly(X). % alas, porcupines don't make good pets
mammal(X) :-
human(X);
dog(X);
cat(X);
bear(X);
bat(X);
catamount(X);
porcupine(X).
friendly(X) :-
human(X);
dog(X);
cat(X);
porcupine(X). % really, they are!
prickly(X) :-
porcupine(X).
human(jim).
dog(fido).
cat(esmerelda).
bat(victor).
catamount(rally). % with some license
porcupine(egbert).
% If we have any unnamed critters, we need this:
:- dynamic bear/1.
% A dynamic declaration tells SWI-Prolog the predicate exists but has no clauses (so it simply fails if no match).Then, at the swipl console, we could write some queries.
?- pet(jim).
false.
?- pet(fido).
true.
?- pet(rally).
false.
?- pet(egbert).
false.In each of the examples above, we’re asking Prolog if these are pets. Now let’s see what happens if we use a variable rather than an atom in our query.
?- pet(X).
X = fido ;
X = esmerelda ;
false.What happened here? The query pet(X) is asking “What X is a pet?” Prolog works to find how this query can be satisfied. At first, it finds that fido is a pet. Then it pauses, offering the user the option to continue to see if there are other possibilities, or terminating the query. In the example above, the user has opted to continue by typing a semicolon which signifies logical OR. Prolog continues and finds that esmerelda also is a pet. When the user asks for more, again, by typing a semicolon, Prolog ends with false.. This indicates that there are no more pets that Prolog knows about.
Notice that in the code, there’s nothing that explicitly tags a given entity as a pet. Instead, we give some facts and rules, and Prolog infers from these what is and is not a pet. The crucial rule is
pet(X) :-
mammal(X), % yeah, we're ignoring parakeets and turtles
\+human(X), % humans can't be pets
friendly(X), % otherwise, why bother?
\+prickly(X). % alas, porcupines don't make good petsThis tells Prolog that a pet is a friendly, non-human mammal, that’s not prickly.
Predicates, functors, and arity
In Prolog, we have functors and predicates. A functor is a syntactic element used to construct compound terms. A predicate is a functor that’s used to define a relation that can be called as a goal.
% functor
point(3, 4) % a data structure representing a point in coordinate space
% predicate
likes(mary, pizza) % a relation that can be used as a fact or part of a queryProlog provides a great many built-in functors and predicates. These have an identifier and a number of arguments (as shown above). Prolog supports arity overloading, so functors and predicates are distinguished by their number of arguments. We refer to functors and predicates by indicating their arity.
point/2is a functor with the identifierpointwhich takes two arguments. We could distinguish this from a different functor which takes three arguments to construct a point in three-dimensional space,point/3.likes/2is a predicate with two arguments.
Unification
In Prolog, = is not assignment, it is unification. Given two variables, X and Y,
X = Y.unifies X and Y. That is, after unification the two variable names X and Y reference the same variable. This means that the following are equivalent:
X = Y.
Y = X.Unification and backtracking are essential aspects of how Prolog works.
In the context of our earlier query pet(X), Prolog seeks to find what can be unified with X to satisfy the query.
We’ll see more about backtracking later.
Arithmetic evaluation and unification
Because unification is not the same as assignment, and because we often wish to evaluate some arithmetic expression and unify the result with some variable, Prolog provides is/2. is/2 evaluates the expression on the right-hand side and unifies it with the left-hand side.
?- X is 1 + 2.
X = 3.Notice that this is very different from the result we get if using =/2.
?- X = 1 + 2.
X = 1+2.In the first case, with is/2, the expression 1 + 2 is evaluated first. In the second, with =/2, the expression is not evaluated, and X is unified with the expression 1 + 2.
A final example should drive the point home:
?- X = 1+2, Y is X.
X = 1+2,
Y = 3.Comparison
Prolog supports comparison with (mostly) familiar operators, although comparison operators differ depending on whether we’re performing arithmetic comparison, in which operands must evaluate to numbers, and term comparison, in which we compare terms with respect to standard ordering.
Arithmetic comparison operators
X > Y % true if X is greater than Y
X < Y % true if X is less than Y
X >= Y % true if X is greater than or equal to Y
X =< Y % true if X is less than or equal to Y
X =\= Y % true if X does not equal Y
X =:= Y % true if X equals YTerm comparison operators
X == Y % true if X and Y are identical
X \== Y % true if X and Y are not identical
X @< Y % true if X precedes Y in standard order
X @> Y % true if X follows Y in standard order
X @=< Y % true if X precedes Y in standard order or X == Y
X @>= Y % true if X follows Y in standard order or X == YThere’s an alternative syntax for testing equivalence using ==/2 and \==/2.
==(X, Y) % true if X equals Y
\==(X, Y) % true if X does not equal YLists
Prolog supports lists. The response to a query might be a list. We often construct lists to satisfy some query. Prolog lists are written with square brackets.
The empty list is as you’d expect: [].
Lists do not work the way you’d expect them to in imperative languages. We construct and deconstruct lists with a common syntax.
[H | T] % head | tail, or if you prefer
[Head | Tail]We refer to the first element of a list as the head (H in the example above), and we refer to the rest of the list as the tail (T in the example above). Notice that the list appears within square brackets, and the head and tail are separated by the vertical bar, |. Whether this constructs a list or deconstructs a list depends on context and usage.
Deconstruction
This deconstructs the list on the right-hand side into head and tail.
?- [H | T] = ['a', 'b', 'c', 'd'].
H = a,
T = [b, c, d].Construction
Here’s an example of constructing a list.
?- H = 'a', T = ['b', 'c'], List = [H | T].
H = a,
T = [b, c],
List = [a, b, c].Notice that the only difference between deconstruction and construction is that [H | T] is on the right-hand side of = for construction, and [H | T] is on the left-hand side for deconstruction.
Recursive construction
Here’s an example of constructing a list recursively. We’ll construct lists of all the natural numbers from some positive integer N down to 1.
% Recursive list construction
range(1, [1]).
range(N, [N | T]) :-
N > 1,
M is N - 1,
range(M, T).Here’s a quick demonstration, assuming this file is saved as “make_list.pl”. (If you wish you can save the snippet of code above to a .pl file and consult the file.)
?- [make_list].
true
?- range(1, List).
List = [1]This hits the base case and returns the result for the base case, the list containing the integer 1. The variable List has no value until Prolog unifies it with [1]. We call a variable without a value free or unbound.
Notice that Prolog’s response is not followed by a full-stop. This is called a decision point, and it allows a user to see if there are other ways of satisfying a given query. Here, we know there aren’t, so we can go ahead and type the full-stop to terminate the query.
?- range(1, X).
X = [1] .Now let’s try with a different positive integer, say, five.
?- range(5, X).
X = [5, 4, 3, 2, 1] . % again, the user supplies . to terminate the queryThere’s a lot going on here, so let’s unpack it carefully.
range/2 is user-defined—this is not calling some built-in. The definition is in two parts: a base case and a recursive case.
The base case is given by
range(1, [1]).When we issue the query range(1, List)., Prolog finds a match in the base case—the first argument is 1. To satisfy the query, the variable List is unified with [1], and we get the result:
?- range(1, List).
List = [1] .Things get more interesting when we issue the query range(5, List).. Now, clearly this does not match the base case, so Prolog moves on to the recursive case.
range(N, [N | T]) :-
N > 1,
M is N - 1,
range(M, T).Initially, the variable N is unified with 5, but the second argument in the query was the variable List. Within the body of the rule, there’s a check N > 1. This fails if N is less than or equal to one, providing a crucial guard. If this evaluates to true then Prolog moves on to the next line M is N - 1 (remember , in Prolog is logical AND). This evaluates N - 1 and unifies this with M. Then, a recursive call is made. The recursive call is range(M, T), which with M = 4 becomes range(4, T). Notice that T is still unbound—it will only get a value once the recursion bottoms out.
This process repeats: Prolog tries to satisfy range(4, T), finds the recursive case again, computes M is 4 - 1, and calls range(3, T). Then range(2, T), and finally range(1, T). At that point, the first argument is 1, which matches the base case: range(1, [1]). So T is unified with [1], and the deepest recursive call is satisfied.
Now the recursion unwinds. Each pending call was waiting on the result of the call below it, and each has the form [N | T] in the head. As we return back up the call stack, the list gets built up one element at a time:
range(1, T)returnsT = [1]range(2, [2 | T])withT = [1]gives[2, 1]range(3, [3 | T])withT = [2, 1]gives[3, 2, 1]range(4, [4 | T])withT = [3, 2, 1]gives[4, 3, 2, 1]range(5, [5 | T])withT = [4, 3, 2, 1]gives[5, 4, 3, 2, 1]
The final result is unified with List in the original query, giving us List = [5, 4, 3, 2, 1].
The key insight is that the list is not built on the way down into the recursion—it is built on the way back up. Each recursive call contributes one element to the front of the list via the [N | T] constructor in the head of the rule, but that element can only be prepended once T has been determined by the call below it.
This pattern—a base case that returns a seed value, and a recursive case that prepends to a tail returned by a deeper call—is one of the most common idioms in Prolog list processing.
Notice that Prolog determined which rules to apply (based on pattern matching). There’s no written flow of control as there would be in an imperative language.
Prolog has some built-ins that come in handy when it comes to lists. The predicate member/2 allows you to check for membership in a list. For example,
?- member(X, SomeList).yields true if X is a member of SomeList and false otherwise.
Recursion
You might ask, “Why don’t we just use a loop?” The answer may surprise you: Prolog does not have loops! The only iteration mechanism Prolog has is recursion.
We saw in the example above, how Prolog constructs a list recursively. Like most programming languages that support recursion, Prolog uses a call stack to keep track of the execution of predicates, including recursive calls. Here’s how it works in Prolog:
Call stack during recursion: When Prolog encounters a predicate call, like range(3, List), it pushes that call onto the call stack. For each recursive call, a new stack frame is pushed onto the call stack. The stack frame contains information about the current state of the execution, including variables, the current goal being processed, and how to resume after the call completes.
Unwinding the call stack: When the base case of a recursive predicate is reached, Prolog starts to unwind the call stack. As it returns from each recursive call, it pops a frame off the stack, resolves the variables for that call, and continues with the remaining logic in the predicate.
Backtracking
Prolog’s call stack is also used to manage backtracking. Backtracking is one of the most important aspects of how Prolog finds satisfaction of queries. If a predicate call fails, Prolog pops the stack frame and goes back to the previous decision point, trying alternative solutions.
We must consider backtracking when ordering clauses within the body of a rule.
In Prolog we can have multiple clauses for the same predicate. Let’s revisit the earlier example of greetings.
greeting(arabic, 'السلام عليكم').
greeting(chinese, '你好').
greeting(english, 'Hello').
greeting(french, 'Bonjour').
greeting(german, 'Guten tag').
greeting(hindi, 'नमस्ते').
greeting(japanese, 'こんにちは。').Let’s say we issue the query
?- greeting(japanese, X).
X = 'こんにちは。'To satisfy this query, Prolog searches the database from top to bottom, trying each greeting clause in turn. It first tries greeting(arabic, 'السلام عليكم'). Prolog attempts to unify the first argument arabic with japanese. This fails, so Prolog backtracks and tries the next clause: greeting(chinese, '你好'). Again, chinese does not unify with japanese, so Prolog backtracks again. This continues until Prolog reaches greeting(japanese, 'こんにちは。'). Here, japanese unifies with japanese, and X is unified with 'こんにちは。'. The query succeeds with no remaining choice points, since there are no further greeting clauses.
We can make backtracking more visible by using a variable for the first argument instead:
?- greeting(L, _).
L = arabic ;
L = chinese ;
L = english ;
L = french ;
L = german ;
L = hindi ;
L = japanese.Each time the user types ;, Prolog backtracks to try the next greeting clause. When there are no more clauses to try, the query terminates.
Prolog uses backtracking to implement what other languages do with conditionals or dispatch. Notice that there are no explicit control-of-flow structures at work here. Prolog does this all on its own.
Collecting query results with findall/3
findall/3 is a built-in predicate for collecting multiple solutions into a list, rather than presenting them one at a time interactively.
findall/3 has the form findall(Template, Goal, List). It finds all solutions to Goal, collects the value of Template for each solution, and unifies the resulting list with List. If there are no solutions, List is unified with the empty list [].
Using our greeting example:
?- findall(L, greeting(L, _), Languages).
Languages = [arabic, chinese, english, french, german, hindi, japanese].Printing to console
write/1 writes a term to the console. nl/0 writes a newline. They are often used together:
?- write('Hello, World!'), nl.
Hello, World!
true.A convenient shorthand is writeln/1, which writes a term followed by a newline in a single call:
?- writeln('Hello, World!').
Hello, World!
true.These are useful for printing intermediate values when debugging or when you want to display results from within a rule rather than relying on Prolog’s built-in answer reporting. For example:
print_greeting(Language) :-
greeting(Language, G),
write(Language), write(': '), writeln(G).Now let’s try it out.
?- print_greeting(french).
french: Bonjour
true.Tracing, spying, and debugging
trace/0 puts SWI-Prolog into trace mode, which prints each step of execution as Prolog works to satisfy a query. This is invaluable when a program isn’t behaving as expected.
?- trace.
true.
[trace] ?- greeting(french, X).
Call: (10) greeting(french, _G123) ? creep
Exit: (10) greeting(french, 'Bonjour') ? creep
X = 'Bonjour'.At each step, Prolog prints a port label and the current goal. The four ports you’ll see most often are:
- Call — Prolog is about to attempt the goal.
- Exit — the goal succeeded.
- Fail — the goal failed; Prolog will backtrack.
- Redo — Prolog has backtracked and is retrying the goal.
When prompted with “?”, pressing “Enter” (or typing “c”) moves to the next step (hence “creep”).
Other options include “s” to skip into a sub-goal, “a” to abort, and “h” for a full list of commands.
Turn tracing off with notrace/0.
[trace] ?- notrace.
true.A useful complement is spy/1, which sets a spy point on a specific predicate so that tracing only activates when that predicate is called, rather than for every step. This is handy in larger programs where full tracing produces too much output:
?- spy(greeting/2).Remove a spy point with nospy/1, or clear all spy points with nospyall/0.
debug/0 and nodebug/0 toggle debug mode, which is a lower-noise alternative to full tracing. In debug mode, Prolog only pauses at spy points you have set with spy/1, and it runs normally everywhere else.
The typical workflow is:
- Set a spy point on the predicate you’re investigating.
- Enable debug mode.
- Issue your query; Prolog runs silently until it hits the spy point, then shows trace output.
?- spy(pet/1).
% Spy point set on pet/1
true.
?- debug.
true.
[debug] ?- pet(X).
Call: (10) pet(fido) ? creep
Exit: (10) pet(fido) ? creep
X = fido ;
Redo: (10) pet(_G123) ? creep
Call: (10) pet(esmerelda) ? creep
Exit: (10) pet(esmerelda) ? creep
X = esmerelda ;
...Turn debug mode off with nodebug/0.
[debug] ?- nodebug.
true.The distinction between trace and debug is one of granularity. Use trace when you want to step through every single goal and see the full execution path. Use debug with spy points when you already have a hypothesis about where things go wrong and want to focus only on a specific predicate without wading through unrelated output.
Documentation and help
The official SWI-Prolog documentation is available at https://www.swi-prolog.org/.
For help on Prolog language features you can use apropos/1 to print portions of the built-in Prolog manual. For example,
?- apropos(member).
% LIB member/2 True if Elem is a member of List.
% SWI memberchk/2 True when Elem is an element of List.
% LIB max_member/2 True when Max is the largest member in the standard order ...
% LIB min_member/2 True when Min is the smallest member in the standard orde ...
% LIB max_member/3 True when Max is the largest member according to Pred, wh ...
% LIB min_member/3 True when Min is the smallest member according to Pred, w ...
% LIB hub_member/2 True when Id is a member of the hub HubName.
% LIB rdf_member/2 True when Member is a member of RDFList
% LIB rdfs_member/2 True if rdf(Container, P, Elem) is true and P is a contai ...
% LIB rdfs_member/2 Test or generate the members of Set.
% LIB fdset_member/2 The integer Elt is a member of the FD set Set.
% LIB ord_memberchk/2 True if Element is a member of OrdSet, compared using ==.
% LIB random_member/2 X is a random member of List.
% LIB directory_member/3 True when Member is a path inside Directory.
% SWI trie_gen/2 True when Key is a member of Trie.
% LIB rdfs_container_membership_property/1 True when Property is a container membership pro ...
% LIB rdfs_container_membership_property/2 True when Property is the Nth container membersh ...
% SEC 'clpfd-membership' Membership constraints
% LIB gen_nb_set/2 Generate all members of Set on backtracking in the standa ...
% SWI apply/2 Append the members of List to the arguments of Goal and c ...
% Showing 20 of 131 matches
%
% Use ?- apropos(Type:Query) or multiple words in Query
% to restrict your search. For example:
%
% ?- apropos(iso:open).
% ?- apropos('open file').
true.You can also use the Prolog built-in help/1. For example,
Availability: :- use_module(library(lists)). (can be autoloaded)
member(?Elem, ?List)
True if Elem is a member of List. The SWI-Prolog definition differs from the classical
one. Our definition avoids unpacking each list element twice and provides determinism
on the last element. E.g. this is deterministic:
member(X, [One]).
author
Gertjan van NoordWhen you’re done with a help topic, type q to quit.
If you’re looking for a good external resource on Prolog visit: The Power of Prolog.
ISO definitions
- atom: A basic object, denoted by an identifier
- atomic term: An atom or a number.
- body: A goal, distinguished by its context as part of a rule.
- clause: A fact or a rule. A rule has two parts: a head, and a body. A fact is a clause with only a head.
- compound term: A functor of arity N, N positive, together with a sequence of N arguments
- database: The set of user-defined procedures which currently exist during execution.
- functor: An identifier together with an arity.
- functor name: The identifier of a functor.
- goal: A predication which is to be executed
- ground: An atomic term or a compound term whose arguments are all ground. A term is ground with respect to a substitution if application of the substitution yields a ground term.
- head (of a rule): A predication, distinguished by its context.
- identifier: A basic unstructured object used to denote an atom, functor name or predicate name.
- instantiated: A variable is instantiated with respect to a substitution if application of the substitution yields an atomic term or a compound term. A term is instantiated if any of its variables are instantiated.
- predicate: An identifier together with an arity.
- predication: A predicate with arity N and a sequence of N arguments.
- rule: A clause whose body is not the goal true. During execution, if the body is true for some substitution, then the head is also true for that substitution. A rule is represented in Prolog text by a term whose principal functor is (:—) / 2 where the first argument is converted to the head, and the second argument is converted to the body.
- uninstantiated: A variable is uninstantiated when it is not instantiated.
- variable: An object which may become instantiated to a term during execution,
References
- Bratko, Ivan. 2012. Prolog Programming for Artificial Intelligence, 4th ed., Pearson.
- Clocksin, William F., and Mellish, Christopher S. 2003. Programming in Prolog: Using the ISO Standard, 5th ed., Springer-Verlag.
- ISO/IEC 13211-1:1995 Information technology – Programming languages – Prolog – Part 1: General core.
- Matuszek, David. 2012. A Concise Guide to Prolog.
- Triska, Markus. 2022. The Power of Prolog.. (Highly recommended.)
- About Backus-Naur form on Wikipedia.
Copyright © 2023–2026 Clayton Cafiero
No generative AI was used in producing drafts of this material. This was written the old-fashioned way. AI was used to rewrite existing pseudocode in LaTeX to produce standalone *.tex files for rendering, and for revisions toward satisfying WCAG 2.1 AA-level accessibility standards as required by UVM policy. It may also have been used to proofread selected human-written prose. Claude 2.1.150 with model Sonnet 4.6. Revisions, if any, were performed by the author. AI was not used in generating any code whatsoever. All code samples and starter code are by the author only.
Comments
In Prolog, inline comments start with
%and continue to the end of the line. Document level comments may appear within these familiar delimiters:/* <comment> */.Examples: