Does anyone know the reason why predicate nb_setarg/3
does not work correctly when used with the predicate forall/3
in the toplevel of the SWI-Prolog interpreter (v. 8.2.1)?
How it works when used in a goal typed in toplevel:
?-
functor(A, array, 5),
forall(arg(Index, A, _),
nb_setarg(Index, A, 0)).
A = array(_26341340, _26341342, _26341344, _26341346, _26341348).
How it works when used in a rule:
new_array(A,N) :-
functor(A, array, N),
forall(
arg(Index, A, _),
nb_setarg(Index, A, 0)).
Then:
?-
new_array(A,5).
A = array(0, 0, 0, 0, 0).
Jan Wielemaker says in issue #733 ("Called from forall/2, nb_setarg/3 applied to a constructed compound term in a toplevel goal has no effect (it becomes setarg/3?") that this is a known problem:
The problem boils down to:
?- functor(C, a, 2), \+ \+ (arg(1, C, x), nb_setarg(1, C, foo)). C = a(_52710, _52712).
I.e., if there is an earlier trailed assignment on the target location, backtracking to before this trailed assignment turns the location back into a variable. In this case the
arg(I,Term,_)
unifies the target with the variable in the toplevel query term. As this is older, the target location becomes a trailed reference to the query variable.
nb_setarg/3
is useful, but a can of worms.
...
I'd have to do a lot of research to find [what is going wrong]. Tracking all the trailing and optimization thereof is non-trivial. Basically, do not backtrack to before where you started using
nb_*
and only use thenb_*
predicates on the same location.
So the idea seems to be that the trail (i.e. the stack of modifications to be roll-back on backtracking if I understand correctly) contains an assignment (arg(1, C, x)
) for exactly the position that is modified with a nb_setarg/3
and you backtrack to before that assignment, your nonbacktrackable assignment is lost.
That makes sense, and seen this way
A = array(_26341340, _26341342, _26341344, _26341346, _26341348).
is actually the correct outcome.
(Switching between logical Prolog and assignment-Prolog makes my head hurt).
I guess this is the way to do it:
A=array(_,_,_,_,_),
forall(
between(1,5,Index),
nb_setarg(Index, A, bar)).
or
functor(A, array, 5),
forall(
between(1,5,Index),
nb_setarg(Index, A, bar)).