Accessing prior bound value setarg

Hi,

Consider the following code:

test_arg :-
      Term = f(_),
       setarg(1, Term, a),
       setarg(1, Term, b),
       setarg(1, Term, c),
      get_prior_val(Term, 1, Val1),
      get_prior_val(Term, 2, Val2),
      Val1 = b.
      Val2 = a.

Is it possible to obtain the prior val of Term, as suggested in the code above – i.e. to retrieve Term’s value in a prior frame …

I guess, something like this is possible, since in the debugger one can view the prior binding of variables by clicking on an earlier location in the execution stack …

How is it done?

I am mainly interested in this for testing purposes …

thanks,

Dan

You can simulate historized variables as follows:

get_current_value([X|L], Y) :- var(L), !, X = Y.
get_current_value([_|L], X) :- get_current_value(L, X).

set_new_value(L, X) :- var(L), !, L = [X|_].
set_new_value([_|L], X) :- set_new_value(L, X).

get_previous_value([X,_|L], Y) :- var(L), !, X = Y.
get_previous_value([_|L], X) :- get_previous_value(L, X).

Works like a charm:

?- set_new_value(X,3), get_current_value(X,Y).
X = [3|_],
Y = 3.

?- set_new_value(X,3), set_new_value(X,4), 
    get_current_value(X,Y), get_previous_value(X,Z).
X = [3, 4|_],
Y = 4,
Z = 3.

?- set_new_value(X,3), 
   (set_new_value(X,4), get_current_value(X,Y);  get_current_value(X,Y)).
X = [3, 4|_],
Y = 4 ;
X = [3|_],
Y = 3.

Not extremly efficient, but the gist of how many things can be implemented under the hood, like backtrackable global variables or updatable attributed variables, and were implemented in early Prolog systems. Your Prolog system might do it more efficiently for you.

Edit 30.11.2021:
Another approach would be Picat canonical rewriting, at least I imagine access to previous value might be easy. Going more back in the past, accessing yesterday yesterdays values on the other hand possibly needs a log as in the above.

Such a log puts pressure on garbage collection, for example when an old value isn’t needed, but nevertheless kept and not reclaimed in deterministic code. Your Prolog system might have some special garbage collection for attributed variables, although this is often not really needed,

since backtracking search anyway quickly rolls back the log.

sorry, fixed it – to setarg –

In the setarg docu it says:

" Extra-logical predicate. Assigns the Arg-th argument of the compound term Term with the given Value. The assignment is undone if backtracking brings the state back into a position before the setarg/3"

So, this should behave quite similar to, say, assoc with a current and next state …

put_assoc (+Key, +Assoc0, +Value, -Assoc)

My intent is to replace an assoc and related put_assoc – which is a key value store – with setarg – which is an array – while relying on setargs backtracking:

Something like this:

	global_struct(global(Arg1, Arg2), 
		Arg1, Arg2.

     set_arg1(Global, NewVal) :- setarg(Global, 1, NewVal).
     set_arg2(Global, NewVal) :- setarg(Global 2, NewVal).

     init_global(Global) :-
                      global_struct(Global), default_1, default_2).
                     
   goal_predicate(Global) :-
                    some further process ... that generates a New_Val
                    set_arg1(Global, New_Val),

I don’t understand exactlly where you are going so will not try to answer.

With regards to arg/3 the post: Does SWI-Prolog have N+K-trees? helped me learn a lot.

In particular the code from Jan W in this reply.

The notes I took in learning to use it in this reply.

HTH

Are you sure? Seems unlikely to me. Not clear what “earlier location in the execution stack” is anyway. Frames have parents, but the data you see in the parent is in the same state if it is shared with child frames.

Except for backtracking, there is no way to get the previous value. Of course you could implement that. It means scanning the trail until you find a trailed assignment into the target term. That is rather costly. Also, if the are two or more consecutive setarg/1 calls on the same location without a reachable choice point in between, only the first is preserved and the rest is reclaimed by the garbage collector.

If you want an argument with a history, the solution of @j4n_bur53 comes to mind. You could improve on that by using setarg/3 to keep current value at the head. That approach keeps O(1) update and access time at a (small) cost of more expensive trailing by setarg/3 when compared to normal unification. E.g.

fetch_arg(I, Term, Value) :-
    arg(I, Term, [Value|_History]).

update_arg(I, Term, Value) :-
    arg(I, Term, Old),
    setarg(I, Term, [Value|Old]).

Now you can easily access old values. The price is that the value is always embedded in a list. I don’t recall I ever wanted something like this :slight_smile:

Thank you Jan,

Stepping back, I simply wanted to prove via a test that the prior values are preserved after a number of setarg calls – and would “pop” up during backtracking.

The global structure I currently have is based on assoc and hence always has the before and after assoc as argument (i don’t hide them via DCG/monads).

When i revised the code to use backtrackable setarg instead i noticed that only one Global variable is needed as argument – which reminded me of the variable implementation (via <= and => operators) in logtalk.

But, i assumed that setarg manage the before and after internally – and hence wanted to see if i can test for them without triggering a backtracking.

Dan

After seeing the reply by Jan W. I think I see what you seek.

You want to be able to go back in time, an event, and retrieve the value for a term.

I have a similar problem.

While the list structure works, a problem you may encounter later is that you want to know the relationship of two values for a given event. Since the list does not record the event but only the value there is no way to sync up values, unless you add a value to the list for every event meaning that the list will contain an entry for every event and also hold lots of redundant data. So when I record the value I also record the term as you call it and the event, which for now is just a sequential number associated with each event. The data is not kept in a list but some data structures I use are red-black trees or just asserting facts. Yes, this code is not fast but as I often note get the code working correctly first then go after optimizations. The events are actually calls from prolog_trace_interception/4.

When the values are stored as dynamic predicates (facts) and the key, think key value pair, is both the name and event, then a query returns more than just one item for a name. So to get the last value for a name aggregate/3 is used with a max operator.

The point is that until you get to a real world problem with this, there is more to think about than what you asked.

If I understand Terminus DB, this is their ball game.

Thanks.

Yes, i also looked at prolog_trace_interception but didn’t really understand what it does, and how to make use of it.

Ideally, it would be nice to have a way to peek into the inter workings in an easy way – like those CPU emulators that show you registers memory and ALU and control units that do things …

Would actually very nice to have for Prolog as well …

I now noticed how destructive setarg is :slight_smile:

It changes the argument also earlier in the stack in the debug window.

I will look at a different approach, taken from the Crafts book, where avoid setarg and update the “Context Argument” structure by binding an update to another variable like so:

set_global_structure_arg1(global(Arg1, Arg2), New_Global, New_Arg1) :-
           New_Global = global(New_Arg1, Arg2).

Like this i am assured that a new version is created while i can continue to see the old values higher up in the call stack.

I guess overall the setarg version might be faster given its destructiveness – less copying … so, i could go for it later on, if i need to try to increase performance … and given the frequency this call is going to be made.