Search code examples
prologiso-prologprolog-assert

How Prolog's logical update view works for assert and retract?


Can someone please explain the Prolog logical view about assert and retract in details?

For example in code below, in the first run Prolog returns true and in subsequent runs returns false. I don't know why because of Prolog logical view when asserta(nextBound(100)) satisfies, nice(X) is still frozen with values when it started so this change should be ignore and nextbound(100) must be false.

nextBound(10000).

nice(X) :-
   asserta(nextBound(100)),
   retract(nextBound(10000)),
   nextBound(100).

Solution

  • You can do a trace to determine what happens:

    | ?- nice(_).
          1    1  Call: nice(_17) ?
          2    2  Call: asserta(nextBound(100)) ?
          2    2  Exit: asserta(nextBound(100)) ?   <-- 1st assert of netBound(100) succeeds
          3    2  Call: retract(nextBound(10000)) ?
          3    2  Exit: retract(nextBound(10000)) ? <-- retract nextBound(10000) succeeds
          4    2  Call: nextBound(100) ?
          4    2  Exit: nextBound(100) ? <-- Succeeds because netBound(100) now a fact
          1    1  Exit: nice(_17) ?
    
    (1 ms) yes
    {trace}
    | ?- nice(_).
          1    1  Call: nice(_17) ?
          2    2  Call: asserta(nextBound(100)) ?
          2    2  Exit: asserta(nextBound(100)) ?   <-- 2nd assert of netBound(100) succeeds
          3    2  Call: retract(nextBound(10000)) ?
          3    2  Fail: retract(nextBound(10000)) ? <-- retract nextBound(10000) fails
          1    1  Fail: nice(_17) ?
    
    (3 ms) no
    {trace}
    | ?-
    

    You can see that, in the first case, first the nextBound(100) fact is successfully asserted (for the 1st time). Then, the retract(nextBound(10000)) succeeds because nextBound(10000). is a fact which exists in the data. Following that, the query nextBound(100) succeeds because two steps before this fact was asserted into the data.

    On the second execution of nice(_), nextBound(10000) doesn't exist because it was retracted in the first execution, and the code doesn't re-assert it. So, in the second execution of nice(_), retract(nextBound(10000)) fails because the fact nextBound(10000) doesn't exist and the entire second execution of nice(_) fails at that point since backtracking over asserta and retract do not re-execute and produce additional results.

    A listing shows that now there are two nextBound(100) facts, since we've asserted one in each of the two runs of nice(_), and no nextBound(10000) since it was retracted in the first run of nice(_):

    | ?- listing.
    
    % file: user
    
    nice(_) :-
            asserta(nextBound(100)),
            retract(nextBound(10000)),
            nextBound(100).
    
    % file: user_input
    
    nextBound(100).
    nextBound(100).
    
    (1 ms) yes
    | ?-
    

    The logical update view, as stated in the SWI documentation, says, Starting with SWI-Prolog 3.3.0 we adhere to the logical update view, where backtrackable predicates that enter the definition of a predicate will not see any changes (either caused by assert/1 or retract/1) to the predicate.

    In other words, the logical update view prevents the predicate from dynamically changing itself while it's executing. That's not the scenario here.

    In fact, it's critical in Prolog that, during the execution of a predicate, if you assert a fact at one point in the predicate, that result must be visible in it immediately, or the predicate may not be able to function properly. There are many many common library predicates that rely on this behavior.