report(X) :-
suspend(report1(X), 1, X->constrained). % suspend
report1(X) :-
( var(X) ->
writeln(constrained(X)),
suspend(report(X), 1, X->constrained) % re-suspend
;
writeln(instantiated(X)) % die
).
Here we have a goal that keeps monitoring changes to its variables.
To do so, it suspends on some or all of those variables.
When a change occurs, it gets woken, does something, and re-suspends.
The repeated re-suspending has two disadvantages: it can be inefficient,
and the goal does not have a unique identifying suspension that could be
easily referred to, because on every re-suspend a new suspension is created.
% A demon that wakes whenever X becomes more constrained
report(X) :-
suspend(report(X, Susp), 1, X->constrained, Susp).
:- demon(report/2).
report(X, _Susp) :-
( var(X) ->
writeln(constrained(X)) % implicitly re-suspend
;
writeln(instantiated(X)),
kill_suspension(Susp) % remove from the resolvent
).