Learn Prolog Now! stands out in two respects: the targeted audience and the style of the authors. The book is written for non-experts, with the aim to get them interested, even enthusiastic about programming in Prolog. The authors certainly succeed in transferring their own appreciation of the Prolog language to the reader. And they let the reader play the main role in this book: the reader is encouraged strongly to participate actively in the journey through a sequence of interesting first Prolog experiences. The pace of the book is slow so nobody gets lost during that journey, and with only 265 pages, it cannot visit all interesting Prolog places. In fact, it just scratches the surface of Prolog programming. The book is really self-contained and it feels like a self-study book: both were intended by the authors. It is freely available on-line as well, but this review is based mostly on the first edition of the paperback version. Having an on-line version is very nice, but its usefulness would increase if it were keyword searchable. It is indeed a real challenge to find out from the on-line version where e.g. infix operator is mentioned for the first time. There is also an on-line errata list, but it is certainly not complete or up to date: any discrepancies between the book, the on-line version and that errata list are annoying.
The main body of the book consists of twelve Chapters:
Page 7 explains the meaning of a predicate with the two clauses
playsAirGuitar(butch) :- happy(butch). playsAirGuitar(butch) :- listens2Music(butch).as follows: Butch plays air guitar either if he is happy or ... . That phrasing strongly suggests some exclusiveness, i.e., if Butch is both happy and listens to music, he is not playing air guitar. A better wording could be chosen here.
Page 12 introduces compound terms under the name complex terms: an uncommon naming, but shared with the GNU-Prolog manual. However, saying that the functor of playsAirGuitar(jody) is playsAirGuitar and that the functor of a complex term has to be an atom, is unforgivable.
The explanation on consulting a file (and on debugging in a later chapter) show that the authors mean Prolog to be used from a command line, as from an xterm. Such an interface to a Prolog system is certainly very popular with older users, but the new generation might be more attracted and accustomed to clicking their way through a Prolog session.
The member/2 predicate is used as a case study for using lists. The version on page 77 results in a singleton variable warning in almost every Prolog system. It would be nice to warn the reader for this: page 79 talks about singleton variables, but not about the warning. Moreover, the authors condemn the (very good!) programming style that gives names to all variables (even the ones that could be replaced by an underscore). It does not mention the fact that every occurrence of an underscore represents a different variable: this is quite important and comp.lang.prolog shows that newbies get confused easily on this issue.
When students are introduced to arithmetic in Prolog, usually the following two issues come up: operators (like the + in 3+2), and the difference between 3+2 in unification and arithmetic. The first issue is not arithmetic specific, and it would be better to treat operators earlier, in a more neutral context. In that way, the issues are kept separate: that is good, because they are orthogonal! The second issue is often explained by pointing out the difference between ?- X is 3+2. and ?- X = 3+2 (see page 92). These two queries tell only part of the story. Here are two additional queries that complete it: ?- X is a+2. and ?- X = a+2.
Page 93 reads for Prolog, it [3+2] really is the term +(3,2). The authors apparently ascribe a strong reality to Prolog internals, but this reality does not really exist: 3+2 and +(3,2) are just two syntactic forms of the same terms. The explanation of this phenomenon has no place in the arithmetic chapter: it belongs in an earlier section on operators.
Section 2 describes the use of arithmetic for computing the length of a list: the first program shown is a non-tail recursive version of length/2. That in itself is fine, but why say explicitly (page 95) that this length/2 program is efficient? It is not.
Page 98 says that ?- X = b, X 4. fails, but ISO requires it to throw an exception.
Page 99 offers a very good opportunity to introduce the Prolog if-then-else, which is sadly lacking from the book.
It is not clear why the authors chose to present accRev/3 with the clause order as on page 114: it is good practice to write the base case(s) first, and the recursive clauses later, especially when that does not change the operational semantics (for the intended use of the predicate).
In the practical session, the authors ask the reader to 'flatten' a list by removing all the square brackets around any list it contains as elements .... The terminology is clearly inadequate and inaccurate. The authors name this exercise the pons asinorum of Prolog programming. I have never seen flatten/3 referred to as such, but it is indeed an exercise that many beginners struggle with. Shouldn't the asini get some feedback on how well they crossed the pons?
So we are skipping to Chapter 9: it is about A closer
Look at Terms. This time, the operator issue goes under the header of
Terms with a special notation. That is misleading: the special
notation is related to operator declarations (built-in or not) and is
not tied to the term itself. Page 163 contains a funny sentence: Prolog finds the query 2 =:= 3 == =:=(2,3) confusing. No it doesn't:
at most it finds a syntax error! Moreover, what is shown is not a
query. Queries start with ?-.
Page 164 refers to a recursive definition: it is better to name it an inductive definition. Page 172 introduces the univ built-in (=../2) in the form '=..'(arg1,arg2). There is no need for the quotes. At last, section 4 mentions operators with more generality, but quite inappropriately, operator notation is mentioned as being more user-friendly than its [Prolog's] own internal representation. This seems based on the authors' misunderstanding about what an internal representation is, and indeed section 4 explicitly says Internally, Prolog uses the expression is(11,+(2,*(3,3))). As a seasoned Prolog implementor, I swear: this is not true. The same mistake is repeated on page 177 and it even distinguishes between user-friendly and real Prolog notation. One must be careful with such statements, because soon we might see hackers writing everything in the real Prolog prefix-notation.
Page 180 uses display/1: this is not an ISO predicate.
max(X,Y,Y) :- X =< Y. max(X,Y,Y) :- X =< Y, !. max(X,Y,X) :- X > Y. max(X,Y,X) :- X > Y.Just ask the query ?- max(1,A,1). and observe what happens on backtracking.
The max/3 example is elaborated on for explaining red and green cuts, and even the principle of steadfastness, which is good. It would have been even better to follow that principle in code later in the book. Negation by failure is explained with its pitfalls on an example.
One of the uses of dynamic predicates is memoization: this is explained with an example. I made exercise 11.3 (related to memoization) and then compared my solution to the one on page 254 ... the result of the ?- listing query at the bottom of page 215 must also include sigmares(0,0) and sigmares(1,1). And the code shown in the Answers to Exercises should have been structured better, so that it would be clear to the reader that there is a systematic approach to introducing memoization for a (deterministic) predicate.
The explanation of findall/3 and its friends is always challenging:
the authors do a good job in Section 2. Page 212 (using nested
findall/bagof calls with ^
) might be a bit over the top for
many readers.
Page 210 names the fromMartha/1 in the goal findall(fromMartha(X),...,...) a predicate: this is bad
terminology to say the least. Some fine-print about ^
/2 and free
variables in a template is avoided. That is a good choice given the
objectives of the book, but a warning about unexplained issues would be
appropriate. It is impossible to talk about setof/3 without talking about
an order: the authors limited themselves to the order on atoms, and
the order on numbers. Again, just mentioning that Prolog has an order
on (almost) all terms would have been nice.
Page 220 uses tab/1: this is not ISO. Page 222 tells you that append/3 and member/2 are built-ins in SWI-Prolog and not in SICStus Prolog ... but beware! The (current) truth is: in SWI-Prolog, those predicates are in a library that is loaded transparently by default. And in the most recent SICStus Prolog version, they are true built-ins (as a forthcoming revision of ISO Prolog intends them to be).
Page 225 has a program that on backtracking checks for at_end_of_stream of a stream that was closed already: this can easily be prevented by introducing a cut, and removing the negation in the second clause of read_houses/2. That would also conform to the advice given by the authors on pages 198-199. Using an if-then-else would have been even better.
Page 225 also says that get_code/1 returns 97 if the character a is next in the input stream. But ISO says it returns 0'a. The use of explicit character codes as in the checkCharAndReadRest/3 code on page 226 (which is not steadfast either) should be discouraged.
At this point, I became aware of differences between the paperback version and the on-line version: I have not bothered checking the other chapters, but if chapter 12 is symptomatic, the difference is huge. There is even an example in the on-line version (in section 12.2) that is posed as exercise 12.1 in the book ... very confusing.
After chapter 12, there is a section with Answers to Exercises: an absolute must for a self-study book of course. The on-line version does not contain this section. There is also a short Further Reading section: the usual books are pointed at, and unsurprisingly, the early Clocksin & Mellish book is mentioned as it won't take you far beyond Learn Prolog Now!. Indeed, these two books are close in spirit and level, but C&M is rather oriented towards students in computer science while the LPN book in the first place addresses (and probably appeals to) a different crowd.
At the end there is a very short list of Prolog Environments: listed are SWI-Prolog, SICStus Prolog, YAP Prolog and Ciao Prolog. Is is impossible to have an exhaustive list here of course, but one might expect some other freely available systems like XSB, B-Prolog, ECLiPSe and GNU-Prolog. A pointer to the comp.lang.prolog FAQ would also be useful.
The terminology could be improved in several places and made consistent. I found rather few errors and typos: these can be easily fixed, but it is a tedious job to spot them all. Please always show steadfast code. More ISO compliance would be nice. Some missing elementary topics or constructs could be added without losing touch with the audience, e.g., the Prolog if-then-else or the meta-call.
There is one area in which the authors seem to overstep their competence, or at least get carried away by their enthusiasm: Prolog implementation. At several places in the book, there are disturbing remarks about performance and about internal representation. For instance on page 8 we find: the semicolon is more efficient [than using separate rules] as Prolog only has to deal with one rule. That is nonsense. Or on page 29: [Infinite terms ... .] No matter how hard Prolog tries, it can never build one. Still, most Prolog systems these days happily represent infinite terms. Page 33 tells us: when a program is written that makes heavy use of unification, it is likely to be extremely efficient. This is very misleading: naive reverse uses only unification but is extremely inefficient. The same holds for programs using successor arithmetic: lots of unification, very inefficient. The authors might have had a valuable message, but why wrap it in a false slogan? Page 164 and 170 say weird things about lists and ./2. Please reconsider carefully all statements related to performance and internal representation.
The book is not written for academics who seek to understand from the start the relation of Prolog with logic (or other paradigms), and if you read it with an academic attitude, the book will probably get on your nerves. My review certainly reflects that.
The level of the book is spot-on for the intended audience: the beginner. This includes the stumped computer science undergrad in need of a really basic introduction because his professor has jumped to the middle of Sterling & Shapiro in lesson 2, the hobbyist who learns new languages just for fun and the scientist from the human science department who wants to find out whether Prolog might be useful for her research. It is not a surprise that the book - or the on-line version - is used by many people, and in particular by people who need or want to learn Prolog without a teacher in flesh and blood. For people with a more profound interest in programming languages, the book has little to offer.
Learn Prolog Now! is a great first book on Prolog programming, in particular for people who are easily forgotten by academic writers: people with little knowledge about computers, little background in programming and little interest in logical foundations. It certainly lowers the threshold for getting into Prolog: you just download SWI-Prolog, work your way to the first practical session on page 16 of Learn Prolog Now! and your first Prolog queries give their answers. This is a fantastic way to learn a piece of Prolog.