[comp.lang.prolog] retractall

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).