Up Next

18.1  Waiting for Instantiation

Goals that are to be woken when one or more variables become instantiated use the inst list. For instance, the following show how to implement a predicate freeze/2, such that the call freeze(Term, Goal) delays and is woken as soon as any variable in Term becomes instantiated:

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)
    ).

Up Next