LISTING 1.
Bug finding strategy in PROLOG.
find_failing_subgoal( Given_goal, Subgoal_which_is_bug) :-
      this_is_the_problem( Subgoal_which_is_bug), !.
find_failing_subgoal( Given_goal, Subgoal_which_is_bug) :-
      find_failing_subgoal_of_my_clauses(  Given_goal,
                             Intermediate_problem_goal),
      find_failing_subgoal( Intermediate_problem_goalGiven_goal,
                             Subgoal_which_is_bug).

LISTING 2.
Testing for a list of integers
with bug.
integers( []) :-
    !,
    fail.
integers( [H|T]) :-
    integer(H),
    integers(T).

LISTING 3.
Report on bug in Listing 2.
HERE IS THE BUG ANALYSIS :
THE TOP LEVEL BUG...
     The user-supplied goal integers([1]) failed.

GOING DOWN A LEVEL,
  The goal at this level is integers([1]) which failed.
  Clause 2 of integers / 1 failed.
  Here it is with the failing subgoal marked.

  integers([A|B]) :-
        integer(A),
FAILS==>integers(B).

  Instantiated, this subgoal is integers([]).

GOING DOWN A LEVEL,
  The goal at this level is integers([]) which failed.
  Clause 1 of integers / 1 failed.
  Here it is with the failing subgoal marked.

  integers([]) :-
        !,
FAILS==>fail.
  Instantiated, this subgoal is fail.
GOING DOWN A LEVEL,
  You deliberately called the predicate 'fail'.
     -- ( end of this particular possible bug explanation ) --

LISTING 4.
Finding information about the current level bug.
          % reports what has happened at current level,
          % produces a report frame
current_level_bug_report( Goal,
                          Current_level_report) :-
          % see why each clause of the Goal predicate failed
    clause_reports( Goal,
                    Clause_report_list) ,!,
          % produce a bug report from the clause info
    current_level_bug_report_hlpr( Goal,
                                   Clause_report_list,
                                   Current_level_report) .

LISTING 5.
Reporting clause failures.

    % maps a Goal into a list of reports on what happened when you
    % tried to satisfy the Goal using the various clauses of the
    % Goal's predicate
clause_reports( Goal, Clause_report_list) :-
            % get functor and arity from Goal
      functor( Goal, Functor, Arity),    !,
            % get generalized rule head
      functor( Head, Functor, Arity),    !,
            % get clauses which have this functor as head
      findall( X,
               ( clause( Head, Body),
                 X = ( Head :- Body)),
               Clauses),                  !,
            % clean up database
      retractall(zzz_current_goal( _   )),
            % store goal to protect it from unification
      asserta( zzz_current_goal( Goal)),
      gen_clause_reports( Clauses, 1, Clause_report_list),
            % clean up database
      retractall(zzz_current_goal( _   )).

LISTING 6.
Clause-level bug reporting.

clause_report_hlpr(   Clause,  Report ):-
         % get fresh copy of Goal, unaffected by any unifications
         % done in computing previous clause reports
   call( zzz_current_goal( Goal)),
         % get head of rule
   Clause = ( Head :- Body),  !,
          % if the Head matches the Goal...
   (  Head = Goal,
          % stay with this branch
      !,
          % save the fact that the heads match
      Report0 =  [ head_matched : true],
          % collect more info about trying the clause body
      clause_report_hlpr_on_body( Body, 1, Report0, Report)
    ;
          % else if Head does not match Goal
      !,
          % report out that fact
      Report =  [ head_matched : fail ]).
/** clause_report_hlpr_on_body finds info on where the body of a
    rule fails **/
         % debug rule
clause_report_hlpr_on_body( Body , _, Report, _ ) :-
         clause_level_trace([$e clause_report_hlpr_on_body, Body = $,
                      Body,
                      $ Report = $, Report ]),
         fail.
         % quit when a failure is reported
clause_report_hlpr_on_body( _ , _, Report, Report) :-
         clause_has_already_failed( Report ),  !.
         % increment cut counts for cut terms
clause_report_hlpr_on_body( ( Term , Rest), N, Report0, Report) :-
           Term == !,
           !,
           increment_cut_count( Report0, Report1),
           N1 is N+1,
           clause_report_hlpr_on_body( Rest, N1, Report1, Report).
         % recurse on success with next term
clause_report_hlpr_on_body( ( Term , Rest), N, Report0, Report) :-
           call( Term),
           !,
           N1 is N+1,
           clause_report_hlpr_on_body( Rest, N1, Report0, Report).
         % report term failure
clause_report_hlpr_on_body( ( Term , _   ), N, Report0, Report) :-
           !,
           Term_report = [ result : fail,
                           failing_subgoal_number : N,
                           failing_subgoal : Term ],
           frame_unify( Term_report,
                        Report0,
                        Report  ) .
         % increment cut counts for final cut terms
clause_report_hlpr_on_body( !,  _, Report0, Report) :-
           !,
           increment_cut_count( Report0, Report ).
         % report success for clause if last term succeeds
clause_report_hlpr_on_body(   Term , N, Report0, Report) :-
           call( Term),
           !,
           Term_report = [ result : success,
                           number_of_subgoals  : N],
           frame_unify( Term_report,
                        Report0,
                        Report  ) .
         % report term failure
clause_report_hlpr_on_body(   Term , N, Report0, Report) :-
           !,
           Term_report = [ result : fail,
                           failing_subgoal_number : N,
                           failing_subgoal : Term ],
           frame_unify( Term_report,
                        Report0,
                        Report  ) .

LISTING 7.
Example of clause-report analysis.

                % If clause report list is empty,
                % no rules are defined
current_level_bug_report_hlpr(
            Goal,       % the current goal
            [],         % the list of clause reports
                        % the generated current level bug report
            [goal_report(
                  [ call : Goal,
                    result : fail,
                    reason :
                      no_clauses_defined])]) :- !.
                % if only 1 head matches , look in there
current_level_bug_report_hlpr( Goal,
                               Clause_report_list,
                               Bug_report) :-
     findall( Clause,
              (  memb( Clause, Clause_report_list),
                 frame_slot_val(head_matched, Clause, true)),
              [ Head_matched ]),
     bug_report_on_clause( Head_matched, Bug_report0 ),
     frame_unify( [ call : Goal], Bug_report0, Bug_report1),
     frame_info( Bug_report1, _, Slots),
     Bug_report = [goal_report( Slots)], !.
bug_report_on_clause( Clause_report , Bug_report) :-
          frame_slot_val( failing_subgoal, Clause_report, Failure),
          remove_if_slot( head_matched,
                          Clause_report ,
                          What_we_need  ) ,
          frame_info( What_we_need , _, Bug_report  ).
1
LISTING 8.
Backtracking version of member.

memb(H,[H|_]).
memb(H,[_|T]) :- memb(H,T).

/* Special thanks to Andre Vellino at Bell Northern Research in Ottawa,
   ON, for showing me how useful this simple predicate can be. */
anks to Andre Vellino 