Book review

Programming in Prolog. Using the ISO Standard. by William F. Clocksin , Christopher S. Mellish, Springer-Verlag, 2003, ISBN 3-540-00678-8, xiii + 299 pages.


This is the fifth and most recent edition of a legendary book whose first edition dates from 1981. It was probably the first introductory Prolog book and it still is the most gentle introduction to Prolog for everyone, including non-computer scientists. The authors make indeed very little assumptions on the computer science knowledge of the reader and not even on her programming skills. Still, the book covers most of the Prolog language.

The first edition was based on the de facto Edinburgh Prolog standard. Obviously, with the appearance of the ISO Prolog Standard, a new edition of the book needed to adapt to ISO, and that is the reason for the subtitle Using the ISO Standard. Other changes were introduced as well and this review mentions some of the differences, but mainly concentrates on the current contents, independent of the history involved.

The Preface of the book sketches the rationale behind the book and the new edition. It also lists in a table differences between the new and the old editions related to switching from the de facto standard to ISO. These differences are related to syntax and built-in predicates. There are surprisingly few differences and they are small.

The book consists of eleven chapters

  1. Tutorial Introduction
  2. A Closer Look
  3. Using Data Structures
  4. Backtracking and the ``Cut''
  5. Input and Output
  6. Built-in Predicates
  7. More Example Programs
  8. Debugging Prolog Programs
  9. Using Prolog Grammar Rules
  10. The Relation of Prolog to Logic
  11. Projects in Prolog
and 4 appendices:
A
Answers to Selected Exercises
B
Clausal Form Program Listings
C
Writing Portable Standard Prolog Programs
D
Code to Support DCGs

The first chapter in any book on Prolog is the most difficult one: how to introduce gradually a whole bunch of connected concepts (and terminology) without getting sidetracked or forgetting certain essentials. The authors do a very good job here: after general remarks about objects and relationships, and the nature of programming, (ground) Prolog facts are introduced as a means to model the knowledge contained in simple English sentences. This is followed immediately by showing how such facts can be queried. Newbie questions in comp.lang.prolog show that at this point, the book would benefit from an explicit statement that facts like likes(john,mary). are not in the Prolog database at startup (and refer to the relevant section in the book). This chapter next discusses variables, conjunctions (in queries), a first example where backtracking happens, and rules. At the end of this chapter, the reader is familiar with datalog. A set of exercises concludes this chapter.

Chapter 2 starts by taking a closer look at the syntax of terms and clauses. There is a lot of fine-print in Prolog syntax, but most programmers never need to know it, so the book justifiably does not go into all the details. That it does not mention the ISO syntax for character codes (like 0'a for the ASCII 97), seems however a more serious oversight, especially in view of the footnote on page 229 which suggests that the notation "a" for [97] should be used in some implementations: "a" is legal syntax but its meaning is not defined by ISO. Equality of terms (unification or matching) and arithmetic are also covered explicitly at this stage. Most importantly, chapter 2 introduces diagrams with boxes containing a goal and an arrow that illustrates the flow of control: this visualization of the execution of Prolog is extremely helpful and is later in the book used for explaining other concepts like backtracking, the cut and debugging. In the first edition, these diagrams looked like a wiggly snake in a Prolog database: the current form is much better.

Chapter 3 introduces data structures which are drawn as trees of Prolog terms and lists. The first recursive predicate now follows naturally: member/2. The chapter goes on with recursive predicates and compound data structures. At the end, we find two new sections on accumulators and difference structures.

There are two negative points about chapter 3: on page 56, the original edition contained an explanation which (rather strangely) links left-recursive rules to the order of rules. In the current edition, two code snippets were switched without adapting the text and it now makes even less sense than before. On pages 59/60, there is an albeit nice example which would have been worth reconsidering in the section on cut. Or at least in the section on cut, the reader could have been asked to reconsider the code on pages 59/60.

Chapter 4 explains in detail backtracking and the cut. Understanding backtracking is central to programming in Prolog and the explanation of backtracking is repeated in the context of several concrete programming examples later in the book as well. This is nice work: it makes use of the diagrams introduced in chapter 2, this time with nested boxes. The cut is also explained very well and with the same diagrams. This is also the natural place to introduce the Prolog negation \+/1. The authors devote some space to the common uses and the pitfalls of the cut. In particular they show that a cut should be placed before the output unification (an issue related to steadfastness), but too many of the example predicates in the book are not steadfast! Another critique related to cuts in the programs in the book, is that there are so many of them. The book would have gained a lot if the Prolog if-then-else construct would have been introduced in this very same chapter, and if subsequently a series of quite ugly programs had been cleaned up. One more negative point: on page 89, the text seems to argue that placing a cut in the body of the first clause (the fact) of append/3, will increase space and time efficiency. This is not true. It is understandable that the authors have chosen not to talk about first argument indexing, but that does not justify wrong statements. Another one of that kind (related to the silence about indexing) can be found on page 146 where it is strongly suggested that Prolog must do a linear search in a database of ground facts, even if the first argument is instantiated.

A chapter about input and output is unavoidable in an introductory textbook on any programming language: it is never exciting and the current book makes no exception, but the example programs are OK. This chapter is adapted to the ISO standard, albeit in a minimalistic way: the authors stick to the DEC-10 see/seeing style which changes the current streams using the ISO built-ins current_input/1 and set_input/1, warning for the pitfalls, but failing to point out the more safe I/O predicates that take the stream as their first argument. At the end of this chapter, there is a list of most important ISO Prolog operators: for some reason the quoting of the operators is inconsistent and backslashes in quoted atoms were not doubled. Also, the ISO style of op/3 with a list of atoms in the third argument should have been followed. Still related to operators: on page 178 it is written that we shall have to declare a ``^'' operator; however, ``^'' is already an ISO operator. The subsequent declaration in the book gives a non-ISO precedence and associativity for ``^'' ...

Chapter 6 goes into more detail about some built-in predicates some of which were covered already earlier in the book. To name just some new ones: var/1 (and friends), clause/2 (and friends), the functor/arg/univ triple, a series of predicates to transform atomic objects to a list of characters, term comparison ... The section on dynamic predicates should have mentioned the logical database update view adopted by ISO Prolog. Page 132 says that the query ?- number_chars(23,X). could deliver the result X = ['2','3','.','0']: that is not true in ISO Prolog. The section named Constructing Compound Goals (which deals with conjunction, disjunction, call/1 and \+/1) should have contained the Prolog if-then-else. The section on arithmetic lists some arithmetic operators and concludes on page 140 with the note: ``Particular Prolog implementations may include more arithmetic operations such as exponentiation and trigonometric functions''. This is misleading as ISO Prolog defines both exponentiation and a whole lot of trigonometric functions, so every ISO Prolog must have them.

Chapter 7 contains a selection of classic programs: a sorted tree, searching in a maze, the towers of Hanoi, dealing with graphs ... Both the problems and the solution programs are nicely laid out: this chapter can be digested by a novice and contains lots of interesting stuff. It is not clear why the authors choose to introduce (the implementation of) findall/3 as an example program, instead of as an extremely useful built-in predicate. Page 147 contains a figure that was adapted from the first version and it looks nicer now, but unfortunately, cut&paste has introduced errors, and made the figure inconsistent with the text. The programs are mostly OK, but cut is used too often and inconsistently. Examples of this are on pages 148 and 165. Many of these programs would benefit from a rewrite in an if-then-else style.

Chapter 8 is about debugging Prolog programs: ISO says nothing about it, but actual implementations do not differ much in their simple interface to the Byrd Box Model. This model is explained very nicely and example traces show how to use it. It includes spy-points and leashing. This chapter contains advice on program layout (which the book regularly does not follow), a section on Common Errors (whose first point is wrong: there is no need to have a return after the last dot in a program - EOF is enough), and a section on Fixing Bugs. The update of the latter section is already 10 years overdue: one cannot seriously describe the use of ?- [user]. (and its pitfalls) as the main method for fixing a program, given the current user interfaces.

Definite Clause Grammars are explained in chapter 9, starting from a simple English sentence analysis. It is a classic and satisfying explanation. The section about Translating Language to Logic is particularly nice, because through DCGs it shows the relation between natural language sentences and logic formulas: this is what logic programming is about!

Chapter 10 explains how Prolog relates to (pure) Logic. A reader who just wants to have a practical grasp on Prolog could skip this chapter, but nobody should be afraid of tackling it: the authors have done a very good job starting from an explanation of Predicate Calculus, going to Clausal Form, covering Resolution and Theorem Provers, finally arriving at Horn Clauses and ending with Prolog. This chapter should be reread regularly by old Prolog programmers.

Chapter 11 lists some small and medium-sized projects to be done by the interested reader: the selection shows some of the areas Prolog is good at.

Appendix A contains the answers to selected problems: Prolog code plus explanation. Not bad, but it should be cleaned up: indentation, layout and cuts are the main issues. Page 269 and 270 introduce some extra typos compared to the first edition, and it is suggested that the notation {X} is not ISO, while it is (page 290 states it correctly though).

Appendix B contains the code for translating a logic formula into clausal form: it follows the explanation of chapter 10, but now we see the full Prolog code.

Appendix C is about Writing Portable Standard Prolog Programs. The only sensible advice would have been to eschew anything that is not defined by the standard, and in particular the issues that ISO has left implementation dependent or implementation defined. Instead, the authors warn the reader that non-conforming implementations might exhibit non-ISO behavior, which is like the empty statement. A section which describes more clearly the differences between (say) DEC-10 Prolog and ISO Prolog would have been more helpful. The programs given in this chapter mimic ISO built-ins using predicates that were popular in the de facto standard: it seems better for the individual programmer not to use these programs.

Appendix D contains the full code for DCG translation. It is not clear why the authors wanted it in the book. Moreover the code and its comments still try to make it work in a non-ISO system: it is one of the examples of the ambiguity of the book which has not resolutely chosen for ISO.


Overall, the book is as great as ever as an introductory text for Prolog. When a newbie asks for an introduction to Prolog, the best advice is still Clocksin&Mellish. Leaving the ISO issue aside, the book would benefit from correcting (sometimes newly introduced) errors, cleaning up some programs (especially the cuts in them) and introducing (and using) if-then-else.

People attracted by the subtitle Using the ISO Standard could feel left in the cold by the actual ISO content of the book. There are many small errors related to ISO in the book. One more example: the footnote on page 182 suggests that an implementation that limits the applicability of clause/2 to dynamic predicates, is not ISO conforming. Since dynamic predicates are public, and since an implementation can limit public predicates to the dynamic ones, it follows that this is not true. There are too many such points in the book.

The book also often fails in Using the ISO Standard in some of its programs. As an example: the preface mentions explicitly that the arithmetic operators =:= and =\= are introduced (they were not in the first edition), but the programs on page 175 and 268 do not use these operators while that would have been the natural thing to do.

On top of that, there are at least two major areas in ISO that the book does not even mention: the first one is the issue of modules. On page 189, the book advises to split a large program over several files, but the concept of modules, neither the fact that ISO Prolog has also a part that standardized modules is mentioned. The second issue is the one of the ISO error mechanism: ISO Prolog prescribes it in detail and beginning programmers are confronted with it from the start because badly called built-in predicates throw an exception. The book should have explained the mechanism: it is not prohibitively difficult. Moreover, on pages 8 and 119, the book mentions only failure, failure+warning or error message as the ISO possibilities for calling a non-existing predicate or calling arithmetic on non-numbers.


In conclusion: this is a great introduction to Prolog, but it is weak on the ISO aspect.


BART DEMOEN
K.U.Leuven, Belgium