Quintus
Prolog Manual
The family of assertion and retraction predicates described below enables you to modify a Prolog program by adding or deleting clauses while it is running. These predicates should not be overused. Often people who are experienced with other programming languages have a tendency to think in terms of global data structures, as opposed to data structures which are passed as procedure arguments, and hence they make too much use of assertion and retraction. This leads to less readable and less efficient programs. An interesting question in Prolog is what happens if a procedure modifies itself, by asserting or retracting a clause, and then fails. On backtracking, does the current execution of the procedure use new clauses which are added to the bottom of the procedure?
Historical note:In early releases of Quintus Prolog, changes to the Prolog database became globally visible upon the success of the built-in predicate modifying the database. An unsettling consequence was that the definition of a procedure could change while it was being run. This could lead to code that was difficult to understand. Furthermore, the memory performance of the interpreter implementing these semantics was poor. Worse yet, the semantics rendered ineffective the added determinacy detection available through indexing.
Quintus Prolog implements the "logical" view in updating dynamic predicates. This means that the definition of a dynamic procedure that is visible to a call is effectively frozen when the call is made. A procedure always contains, as far as a call to it is concerned, exactly the clauses it contained when the call was made. A useful way to think of this is to consider that a call to a dynamic procedure makes a virtual copy of the procedure and then runs the copy rather than the original procedure. Any changes to the procedure made by the call are immediately reflected in the Prolog database, but not in the copy of the procedure being run. Thus, changes to a running procedure will not be visible on backtracking. A subsequent call, however, makes and runs a copy of the modified Prolog database. Any changes to the procedure that were made by an earlier call will now be visible to the new call. In addition to being more intuitive and easy to understand, the new semantics allow interpreted code to execute with the same determinacy detection (and excellent memory performance) as static compiled code (see {manual(b-5-2)}, for more information on determinacy detection).
All Prolog procedures are classified as being either static or dynamic procedures. Static procedures can be changed only by completely redefining them using the Load Predicates ({manual(g-4)}). Dynamic procedures can be modified by adding or deleting individual clauses using the assert and retract procedures. If a procedure is defined by being compiled, it is static by default. If you need to be able to add, delete, or inspect the individual clauses of such a procedure, you must make the procedure dynamic. There are two ways to make a procedure dynamic:
A procedure is declared dynamic by preceding its definition with a declaration of the form:
:- dynamic Pred
where Pred must be a procedure specification of the form Name/Arity, or a sequence of such specifications, separated by commas. For example,
:- dynamic exchange_rate/3, spouse_of/2,
gravitational_constant/1.
where 'dynamic' is a built-in prefix operator. If Pred is not of the specified form an exception is raised, and the declaration is ignored. Note that the symbol ':- ' preceding the word 'dynamic' is essential. If this symbol is omitted, a permission error is raised because it appears that you are trying to define a clause for the built-in predicate dynamic/1. Although dynamic/1 is a built-in predicate, it may only be used in declarations. When a dynamic declaration is encountered in a file being compiled, it is considered to be a part of the redefinition of the procedures specified in its argument. Thus, if you compile a file containing only
:- dynamic hello/0
the effect will be to remove any previous definition of hello/0 from the database, and to make the procedure dynamic. You cannot make a procedure dynamic retroactively. If you wish to make an already-existing procedure dynamic it must be redefined. It is often useful to have a dynamic declaration for a procedure even if it is to be created only by assertions. This helps another person to understand your program, since it emphasizes the fact that there are no pre-existing clauses for this procedure, and it also avoids the possibility of Prolog stopping to tell you there are no clauses for this procedure if you should happen to call it before any clauses have been asserted. This is because unknown procedure catching (see {manual(e-1-4-4)}) does not apply to dynamic procedures; it is presumed that a call to a dynamic procedure should simply fail if there are no clauses for it. If a program needs to make an undefined procedure dynamic, this can be achieved by calling clause/2 on that procedure. The call will fail because the procedure has no clauses, but as a side-effect it will make the procedure dynamic and thus prevent unknown procedure catching on that procedure. See the Reference page for details of clause/2. Although you can simultaneously declare several procedures to be dynamic, as shown above, it is recommended that you use a separate dynamic declaration for each procedure placed immediately before the clauses for that procedure. In this way when you reconsult or recompile the procedure using the editor interface, you will be reminded to include its dynamic declaration. Dynamic procedures are implemented by interpretation, even if they are included in a file which is compiled. This means that they are executed more slowly than if they were static, and also that can be printed out using listing/0. Dynamic procedures, as well as static procedures, are indexed on their first argument; see {manual(b-5-2)}.
A database reference is a term which uniquely identifies a clause or recorded term (see {manual(g-14-7)}) in the database. Database references are provided only to increase efficiency in programs that access the database in complex ways. Use of a database reference to a clause can save repeated searches using clause/2. However, it does not normally pay to access a clause via a database reference when access via first argument indexing is possible. Consistency checking is done whenever a reference is used; any attempt to use a reference to a clause that has been retracted will cause an existence error to be raised. There is no restriction on the use of references. References may be included in asserted clauses. Database references to clauses and in clauses are preserved across saving and restoring via QOF files (see also {manual(h-1-1-2)}). In Release 3, a database reference reads and writes like a Prolog term of the form '$ref'(<integer>,<integer>); however, it is actually represented as a distinguished atomic data type by the Prolog system. As a result, Prolog operations like functor/3 and arg/3 treat database references as they would numbers or atoms:
| ?- assert(foo,M).
M = '$ref'(1296804,1)
| ?- functor('$ref'(1296804,1), N, A).
N = '$ref'(1296804,1),
A = 0
| ?- arg(1, '$ref'(1296804,1), A).
no
| ?-
Database references can be identified using the type test db_reference/1., full indexing is automatically available on the entire database reference, so it is unnecessary to have access to its components. As in the past, the representation of database references may change in future releases, so programs should not rely on it.
The assertion predicates are used to add clauses to the data base in various ways. The relative position of the asserted clause with respect to other clauses for the same predicate is determined by the choice among assert/1, asserta/1, and assertz/1. A data base reference which uniquely identify the clause being asserted is established by providing an optional second argument to any of the assertion predicates.
This section briefly describes the predicates used to remove the clauses and/or properties of a predicate from the system.
Note: Removing all of a predicate's clauses by retract/1 and/or erase/1 (see {manual(g-14-4-1)}) does not remove the predicate's properties (and hence its definition) from the system. The only way to completely remove a predicates clauses and properties is to use abolish/[1,2].
WARNING: retract/1 is a non-determinate 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).
The following examples show what happens when a procedure is modified while it is running. This can happen in two ways:
In either case, the question arises as to whether the modifications take effect upon backtracking into the modified procedure. In Quintus Prolog the answer is that they do not. As explained in the overview to this chapter (see {manual(g-14)}), modifications to a procedure affect only calls to that procedure which occur after the modification.
Consider the procedure foo/0 defined by
:- dynamic foo/0.
foo :- assertz(foo), fail.
Each call to foo/0 asserts a new last clause for foo/0. After the N@+[th] call to foo/0 there will be N+1 clauses for foo/0. When foo/0 is first called, a virtual copy of the procedure is made, effectively freezing the definition of foo/0 for that call. At the time of the call, foo/0 has exactly one clause. Thus, when fail/0 forces backtracking, the call to foo/0 simply fails: it finds no alternatives. For example,
| ?- compile(user). | :- dynamic foo/0. | foo :- assertz(foo), fail. | [% user compiled in module user, 0.100 sec 2.56 bytes] yes | ?- foo. % The asserted clause is not found no | ?- foo. % A later call does find it, however yes | ?-
Even though the virtual copy of foo/0 being run by the first call is not changed by the assertion, the Prolog database is. Thus, when a second call to foo/0 is made, the virtual copy for that call contains two clauses. The first clause fails, but on backtracking the second clause is found and the call succeeds.
| ?- assert(p(1)), assert(p(2)), assert(p(3)).
yes
| ?- p(N), write(N), nl, retract(p(2)),
retract(p(3)), fail.
1
2
3
no
| ?- p(N), write(N), fail.
1
no
| ?-
At the first call to p/1, the procedure has three clauses. These remain visible throughout execution of the call to p/1. Thus, when backtracking is forced by fail/0, N is bound to 2 and written. The retraction is again attempted, causing backtracking into p/1. N is bound to 3 and written out. The call to retract/1 fails. There are no more clauses in p/1, so the query finally fails. A subsequent call to p/1, made after the retractions, sees only one clause.
| ?- compile(user).
| :- dynamic q/1.
| q(1).
| q(2).
| q(3).
| ^D
[% user compiled in modules user, 0.117 sec 260 bytes]
yes
| ?- q(N), write(N), nl, abolish(q/1), fail.
1
2
3
no
| ?-
Procedures that are abolished while they have outstanding calls do not become invisible to those calls. Subsequent calls however, will find the procedure undefined.
The following predicates are provided solely for compatibility with other Prolog systems. Their semantics can be understood by imagining that they are defined by the following clauses:
recorda(Key, Term, Ref) :-
functor(Key, Name, Arity),
functor(F, Name, Arity),
asserta('$recorded'(F,Term), Ref).
recordz(Key, Term, Ref) :-
functor(Key, Name, Arity),
functor(F, Name, Arity),
assertz('$recorded'(F,Term), Ref).
recorded(Key, Term, Ref) :-
functor(Key, Name, Arity),
functor(F, Name, Arity),
clause('$recorded'(F,Term), _, Ref).
The reason for the calls to functor/3 in the above definition is that only the principal functor of the key is significant. If Key is a compound term, its arguments are ignored.
NOTE: Equivalent functionality and performance, with reduced memory costs, can usually be had through normal dynamic procedures and indexing (see {manual(g-14)}, and indexing tutorial in "Writing Efficient Programs section).
In some implementations, database references are also represented by compound terms, and thus subject to the limitation described above. recorda(+Key, +Term, -lab, Ref) records the Term in the internal database as the first item for the key Key; a database reference to the newly-recorded term is returned in Ref. recordz(Key, Term, Ref) is like recorda/3 except that it records the term as the last item in the internal database. recorded(Key, Term, Ref) searches the internal database for a term recorded under the key Key which unifies with Term, and whose database reference unifies with Ref. current_key(*KeyName, *KeyTerm) succeeds when KeyName is the atom or integer which is the name of KeyTerm. KeyTerm is an integer, atom, or compound term which is the key for a currently recorded term.
contact: product
support sales information