Page 1 of 1

retract_exactly/1

Posted: Thu Mar 05, 2009 11:42 am
by Parker
Hi Paulo,

Could you advise me on how to implement Richard O'Keefe's retract_exactly/1? My understanding is that it is still not possible in Logtalk without reaching through to the backend Prolog.

Code: Select all

retract_exactly(Clause) :-
    ( nonvar(Clause) -> Clause = :-(Head,Body)
    ; Head = Clause, Body = true
    ),
    must_be_callable(Head),
    functor(Head, F, N),
    functor(Skel, F, N),
    clause(Skel, Rest, DbRef),
    :-(Skel,Rest) =@= :-(Head,Body),
    erase(DbRef).
Here is the rationale for the retract_exactly/1:
Consider these two cases:
Case 1. clause db contains:
p(a)
p(X)

Now, retract(p(Z)) will remove p(a).

Case 2. clause db contains the same clauses but in reverse order:
p(X)
p(a)

Again, retract(p(Z)) and this time p(X) is removed.

What if a predicate is required that retracts only structurally-equivalent clauses - ie retract_exactly(p(Z)) in both cases above removes p(X) and leaves p(a).
I first raised this on the SWI-prolog mailing list back in 2007 and I still have the issue.
http://osdir.com/ml/lang.swi-prolog.gen ... 00008.html

Just wondering if this can be done in Logtalk yet.

Cheers,
Parker

Re: retract_exactly/1

Posted: Thu Mar 05, 2009 2:57 pm
by Paulo Moura
Parker wrote: Could you advise me on how to implement Richard O'Keefe's retract_exactly/1? My understanding is that it is still not possible in Logtalk without reaching through to the backend Prolog.
Correct. Database predicates such as clause/3 must be handled by the Logtalk compiler in order to translate their arguments (to take into account the way Logtalk compiles object and category predicates) before calling the corresponding Prolog built-in predicates. Predicates such as clause/3 or erase/1 or not standard and are only available in some Prolog compilers. Adding them as Logtalk built-in database methods would require emulating them for Prolog compilers laking the corresponding built-in predicates.

A possible solution would be to use an extra goal in the clause body whose argument would be a unique reference (generated e.g. using the "gensym" library). This goal would be defined by a single clause that would always succeed. Something like this (not tested):

Code: Select all

db_ref(_).

my_assertz((Head :- Body), Ref) :-
	!,
	gensym::gensym('db_ref', Ref),
	assertz((Head :- (db_ref(Ref), Body))).

my_assertz(Fact, Ref) :-
	!,
	gensym::gensym('db_ref', Ref),
	assertz((Fact :- db_ref(Ref))).

my_clause(Head, Body, Ref) :-
	clause(Head, (db_ref(Ref), Body)).

my_retract((Head :- Body), Ref) :-
	!,
	retract((Head :- (db_ref(Ref), Body))).

my_retract(Fact, Ref) :-
	retract((Head :- db_ref(Ref))).

my_retract_exactly(Clause) :-
	(	nonvar(Clause) ->
		Clause = (Head :- Body)
	;	Head = Clause,
		Body = true
	),
	(	callable(Head) ->
		true
	;	this(This),
		sender(Sender),
		throw(error(type_error(callable, Head), This::retract_exactly(Clause), Sender))
	),
	functor(Head, Functor, Arity),
	functor(HeadTemplate, Functor, Arity),
	my_clause(HeadTemplate, BodyTemplate, Ref),
	(HeadTemplate :- BodyTemplate) =@= (Head :- Body),
	my_retract((HeadTemplate :- BodyTemplate), Ref).
Would this solution work in your case?