Nested transaction/1 not behaving as I'd expect

The docs for transaction/1 say that testing transactions is allowed. I assumed this meant that the inner transaction would commit or rollback changes within its scope, and the outer transaction could choose to commit or rollback its changes (including any committed by the inner scope).

I’m having trouble coming up with any model to understand the behavior below…I expected getValues(Set) to return [1, 2, 3], and calling tx_data(X) afterward to return X = 3. This is what happens if I remove either one (or both) of the transaction/1 statements.

Is this behavior expected? Can anyone explain what’s going on?

% Test.pl
:- dynamic tx_data/1.
tx_data(0).

getValues([New1, New2, New3]) :-
    transaction((
        increment(New1),
        increment(New2),
        increment(New3)
    )).

increment(New) :-
    transaction((
        tx_data(Old),
        retractall(tx_data(_)),
        New is Old + 1,
        assert(tx_data(New))
    )).

?- consult('Test.pl').
true.

?- getValues(Set).
Set = [1,1,1].

?- tx_data(X).
X = 1 ;
X = 1 ;
X = 1.
2 Likes

Good question.

I can’t get it to work with nested transactions and based on reading the documentation for transaction/1 your code is what I would expect to work. I can get it to work as you expect with either predicate using transaction/1 but as you demonstrate it does not work when both predicates work as nested transactions .


There are test cases for transactions.

HTH

1 Like

There was a bug that causes a retract in the outer transaction not to be visible in a subsequent nested transaction (i.e., the retracted clauses became visible in the nested transaction). Now works as it should. Thanks for reporting.

P.s. I see patterns like p(X), retractall(p(_)) quite often. The simpler solution is retract(p(X)).

1 Like

For those wanting to look at the patch which also added a test case. :slightly_smiling_face:

Great, Thanks Jan!

Thanks for the pattern tip too. I remember you saying that before, but somehow it is a habit that is hard to get over. Maybe because it feels counterintuitive to me that anything in a “retract” would still be alive after. Makes sense, but hard for me to do for some reason.

It is not alive after the retract. The act of retracting however unifies and the clause with the pattern the unification is not undone when the matched clause is removed. And you can backtrack as well, so to cleanup and get all facts, do

 findall(X, retract(p(X)), Xs).
1 Like