18.1 Waiting for Instantiation
Goals that are to be woken when one or more variables become
instantiated use the inst list.
For instance, a predicate freeze(Term, Goal) which
delays and is woken as soon as any variable in Term
becomes instantiated can be implemented as follows:
freeze(Term, Goal) :-
suspend(Goal, 3, Term->inst).
or equivalently by
freeze(Term, Goal) :-
make_suspension(Goal, 3, Susp),
insert_suspension(Term, Susp, inst of suspend, suspend).
When it is called with a nonground term, it produces a delayed goal
and when one variable is instantiated, the goal is woken:
[eclipse 2]: freeze(X, write(hello)).
X = X
Delayed goals:
write(hello)
yes.
[eclipse 3]: freeze(p(X, Y), write(hello)), X=Y.
X = X
Y = X
Delayed goals:
write(hello)
yes.
[eclipse 4]: freeze(p(X, Y), write(hello)), Y=1.
hello
X = X
Y = 1
yes.
However, if its argument is ground, it will still produce
a suspended goal which may not be what we expect:
[eclipse 5]: 8.
freeze(a, write(hello)).
Delayed goals:
write(hello)
yes.
To correct this problem, we can test this condition separately:
freeze(Term, Goal) :-
nonground(Term),
!,
suspend(Goal, 3, Term->inst).
freeze(_, Goal) :-
call(Goal).
and get the expected results:
[eclipse 8]: freeze(a, write(hello)).
hello
yes.
Another possibility is to wait until a term becomes ground,
i.e. all its variables become instantiated.
In this case, it is not necessary to attach the suspension
to all variables in the term.
The Goal has to be called when the last variable in Term
is instantiated, and so we can pick up any variable and
attach the suspension to it.
We may then save some unnecessary waking when other variables
are instantiated before the selected one.
To select a variable from the term,
we can use the predicate term_variables/2 which extracts
all variables from a term.
However, when we already have all variables available, we can in fact
dispose of Term which may be huge and have
a complicated structure.
Instead, we pick up one variable from the list until
we reach its end:
wait_for_ground(Term, Goal) :-
term_variables(Term, VarList),
wait_for_var(VarList, Goal).
wait_for_var([], Goal) :-
call(Goal).
wait_for_var([X|L], Goal) :-
(var(X) ->
suspend(wait_for_var([X|L], Goal), 3, X->inst)
;
nonground(X) ->
term_variables(X, Vars),
append(Vars, L, NewVars),
wait_for_var(NewVars, Goal)
;
wait_for_var(L, Goal)
).