ok@quintus.UUCP (Richard A. O'Keefe) (03/09/88)
There seems to be some confusion about what the command
retractall(Head)
does/should do. There are apparently two families:
The "Head" family.
In this family,
retractall(Head) :-
clause(Head, _Dont_Care, Ref),
erase(Ref),
fail
; true.
That is, it retracts all the clauses whose HEADS match Head.
Quintus Prolog uses this definition, which we copied from C Prolog,
which got it from NU7 PDP-11 Prolog, which got it from an early
version of DEC-10 Prolog. NU Prolog (from Melbourne) also belongs
to this family. ESI Prolog basically belongs to this family, but
beware of trying to retract clauses from the predicate (/)/2!
The "Clause" family.
In this family,
*retractall(Clause) :-
retract(Clause),
fail
; true.
AAIS Prolog belongs to this family, but then the AAIS manual frankly
calls it "a new dialect" and says "Edinburgh Mode ... does not make
AAIS Prolog 100% compatible". Since assert(Clause,Ref), erase(Ref)
may erase some other clause entirely, this is a true statement!
I do not know which other Prologs belong to this family.
Why have retractall/1 at all? There are two reasons. The first is that
it is usefully different from abolish/2 or abolish/1, and the second is
that there is no portable way of defining it.
Difference from abolish/[1,2].
What's the difference between
abolish(p, 2) or abolish(p/2)
and retractall(p(_,_))?
Very simple. abolish/[1,2] make it as if the predicate had never
existed. All the information which the system has about an abolished
predicate is discarded. Spy-points, dynamic/static flag, any other
information, all of it goes. abolish/[1,2] make a predicate completely
undefined.
retractall(Head) can only affect dynamic predicates (in those systems
which make a distinction between static and dynamic code; note that
this is not intrinsically the same as the distinction between compiled
and interpreted). It removes the clauses whose heads match Head, but
that's all; any other clauses for the same predicate remain intact, any
spy-points or other debugging information remain unchanged, any other
information (such as which file(s) the predicate came from) remains,
even if the last clause goes.
For example, if you have a program which looks like
:- dynamic
p/2.
go :-
{initialise}
{process}
{terminate}.
and either {initialise} or {terminate} clears out p/2, the thing to use
is retractall(p(_,_)) rather than abolish(p,2), because retractall will
just remove the clauses, while abolish will make p/2 undefined.
No portable definition using more basic operations.
Why do I say that there is no portable way of defining retractall/1?
Well, the definition I gave above won't work in a Prolog system which
lacks clause/3, or which, like AAIS Prolog, may erase the wrong clause.
The next obvious thing to try is
retractall(Head) :-
retract((Head :- _Body)),
fail
; true.
but that won't work in some Prolog systems,
which would see this call to retract/1 as
retract((Head :- call(Body))).
Neither will it work in some other Prolog systems, where unit clauses
Head are not identified with Head:-true. (This is not the least of the
reasons for making retractall(Head) operate on heads rather than whole
clauses.) Nor yet will it work in AAIS Prolog, where one has to write
retract((Head :- ... _Body))
Given that the Head version is a useful operation which cannot be
defined portably in terms of other operations, and that the Clause
version is trivially easy to define it you want it, I think it's clear
which version should be provided by Prolog vendors (in the absence of
some method clearly superior to both).