A Note on Efficient Use of retract/1

WARNING: retract/1 is a nondeterminate procedure. Thus, we can use

     | ?- retract((foo(X) :- Body)), fail.
     

to retract all clauses for foo/1. A nondeterminate procedure in Quintus Prolog uses a choice point, a data structure kept on an internal stack, to implement backtracking. This applies to user-defined procedures as well as to built-in and library procedures. In a simple model, a choice point is created for each call to a nondeterminate procedure, and is deleted on determinate success or failure of that call, when backtracking is no longer possible. In fact, Quintus Prolog improves upon this simple model by recognizing certain contexts in which choice points can be avoided, or are no longer needed.

The Prolog cut (!) works by removing choice points, disabling the potential backtracking they represented. A choice point can thus be viewed as an "outstanding call", and a cut as deleting outstanding calls.

To avoid leaving inconsistencies between the Prolog database and outstanding calls, a retracted clause is reclaimed only when the system determines that there are no choice points on the stack that could allow backtracking to the clause. Thus, the existence of a single choice point on the stack can disable reclamation of retracted clauses for the procedure whose call created the choice point. Space is recovered only when the choice point is deleted.

Often retract/1 is used determinately; for example, to retract a single clause, as in

     | ?- <do some stuff>
           retract(Clause),
          <do more stuff without backtracking>.
     

No backtracking by retract/1 is intended. Nonetheless, if Clause may match more than one clause in its procedure, a choice point will be created by retract/1. While executing "<do more stuff without backtracking>", that choice point will remain on the stack, making it impossible to reclaim the retracted Clause. Such choice points can also disable tail recursion optimization. If not cut away, the choice point can also lead to runaway retraction on the unexpected failure of a subsequent goal. This can be avoided by simply cutting away the choice point with an explicit cut or a local cut (->). Thus, in the previous example, it is preferable to write either

     | ?- <do some stuff>
           retract(Clause),
           !,
          <do more stuff without backtracking>.
     

or

     | ?- <do some stuff>
          ( retract(Clause) -> true ),
          <do more stuff without backtracking>.
     

This will reduce stack size and allow the earliest possible reclamation of retracted clauses. Alternatively, you could use retract_first/1, defined in library(retract).