Careful with forall/2 when it is used for "side-effects"

SWI-Prolog Manual said that forall/2 can be used for its side-effects.

The forall/2 control structure can be used for its side-effects. E.g., the following asserts relations in a list into the dynamic database:

But before using it, make sure your side effect is not backtrackable. If it is, the result might not be what you want.

For example,

b_add(N, V) :- b_getval(N, V1), V2 is V1 + V, b_setval(N, V2).

?- b_setval(a,0), forall(between(1,3,X), b_add(a,X)), b_getval(a, R).
R = 0.

Because the side effect is backtrackable, the assignment doesn’t do anything.

Another example is CHR, which is also backtrackable!

:- use_module(library(chr)).
:- chr_constraint a/1.

%% attempt to add three constraints
?- forall(between(1,3,X), a(X)). 
true.

Using foreach/2 if you don’t like this behavior.

?- b_setval(a,0), foreach(between(1,3,X), b_add(a,X)), b_getval(a, R).
R = 6.

?- foreach(between(1,3,X), a(X)).
a(3),
a(2),
a(1).