We now sketch a simple strategy for using the tracer which copes with several of the symptoms described above. First, we handle (apparent) non-termination.
There may be several reasons why a program appears not to terminate. These include factors outside of Prolog --- e.g. the system is down, the terminal screen is frozen and the keyboard is dead. Another factor might be a `bug' in the Prolog system itself. We have four more possibilities: some built-in predicate may not have terminated ( e.g. you are trying to satisfy a read/1 goal but not terminated input properly), you may accidently be writing to a file instead of to the terminal, the program might just be extremely inefficient or, finally, the program is never going to terminate ---real non-termination--- but it is hard to be sure of this!
During the execution of the goal:
If the trace reveals a sequence of repeated, identical subgoals then this suggests that the program will not terminate.
Now, we look at a top-down way of debugging a program for terminating programs. The idea is to examine a goal by looking at each of its subgoals in turn until an error is detected. The subgoal containing the error is then explored in the same way. The basic schema is to
All we suggest is that you examine whether or not a goal succeeds (or fails) when it should, whether or not it binds (or does not bind) those variables which you expect, and whether the bindings are the ones you intended.
We illustrate with a simple program found in figure 5.6.
Figure 5.6: Yuppies on the Move
If the predicate for_yuppies/1 is taken to mean ``a country is suitable for yuppies to live in if it is near Austria and wealthy'' then we might intend that the query for_yuppies(austria) should succeed ---but it does not. We make sure that leash(full) (the default), turn on the tracer with trace and then issue the goal for_yuppies(austria). Using the box model, we should get (in a simpler form than that produced by most tracers):
Call: for_yuppies(austria) ? creepAt this point we know that there is no clause for neighbouring(austria,austria) and we can change the program.
Call: near_austria(austria) ? skip
Fail: near_austria(austria) ? retry
Call: near_austria(austria) ? creep
Call: country(austria) ? skip
Exit: country(austria) ? creep
Call: neighbouring(austria,austria) ? skip
Fail: neighbouring(austria,austria) ?
Note that this is what the Byrd box model predicts, what SICSTUS does but
Edinburgh Prolog produces.
Consequently this strategy,
although eminently sensible, will not work well for Edinburgh Prolog.