I'm trying to understand what assert and retract do with the term they are passed. If I run the following:
?- assertz(item(first)).
true.
?- assertz(item(second)).
true.
?- assertz((item(Y) :- =(Y, third), writeln(Y))).
true.
?- retract(item(X)).
X = first ;
X = second ;
false.
retract/1 removes all of the items but my writeln/1 is never called, so it appears retract is not actually resolving the term it is passed. It looks like it is doing some special operation where it:
- Unifies the term with only facts (i.e. rules with no tail) in the database
- Retracts each one after applying the substitution from unification
Is this right? Or is something else happening here?
Said another way: If I write my own single argument rule, Prolog does not automatically unify item(X) with the database and iterate through all facts that are item/1. It just gives me item(X). How is retract doing its magic?
?- assertz((myRule(X) :- writeln(X))).
true.
? myRule(item(X)).
item(_2556)
true.
Answer with further clarification: Based on the answer by User9213, it appears that the answer is "retract (but not assert!) has a funny behavior where it does a simple unification of its term before it does something to the database". I'm left wondering why other built-in functions that I've seen (like atom/1 and writeln/1) don't appear to do this. Maybe it is more common than I've experienced?
For consistency, it seems like retract should have required ground terms and if the user wanted to do something like my above example, they could have done this:
?- (item(X), retract(item(X))).
It all makes me think I'm missing something or maybe these built in functions were just inconsistently designed? Any clarification that would explain that would be great.
=(Y, third)
works, it's a lot less readable than it's equivalentY = third
, so I'd encourage you to ignore the "canonical" representation of such things and use the more clear one if possible. I think in this case you were trying to avoid some kind of body-inlining optimization, but I don't think there is anything like that; if anything, it probably works the other way around (tryasserta(foo), clause(foo, Body)
and you'll getBody = true
back, implying it was equal tofoo :- true.
) – Daniel Lyonsretract/1
notrevert/1
, right? – lurkeritem(third)
, you're never going to activate the body of the predicate you've added to the dynamic store. Remember that Prolog is not an expression-based language, it does not evaluate nested terms. If you want to call it and then retract it, you have to doitem(third), retract(item(third))
oritem(X), retract(item(X))
because the argument toretract/1
is a term. – Daniel Lyonsitem(X)
in the implementation of retract/1 whenretract(item(X))
was called. If it was resolving it, thenitem(X)
would unify with the ruleitem(Y) :- ...
, Y would get assigneditem(third)
via the unification in the rule, andwriteln(Y)
would get called. Just like if I ran the queryitem(X)
after asserting the items in that example. Or that was my intention at least. – Eric Zinda