17.11 Details of the Execution Mechanism
17.11.1 Particularities of Waking by Unification
Goals that are suspended on the inst or bound waking
conditions are woken by unifications of their
One suspending variable can be responsible for delaying several goals,
on the other hand one goal can be suspended on several
suspending variables (as alternative waking conditions).
This means that when one suspending variable is bound,
several delayed goals may be woken at once.
The order of executing woken suspended goals does not necessarily correspond
to the order of their suspending. It is in fact determined by their
priorities and is implementation-dependent within the same priority group.
The waking process never interrupts unifications and/or a sequence
of simple goals.
Simple goals are a subset of the built-ins and
can be recognised by their call_type
flag as returned by
simple goals having the type external.
Note also that some predicates, e.g. is/2,
are normally in-line expanded and thus simple, but can be regular when
inlining is suppressed, e.g. by the pragma(noexpand) directive.
ECLiPSe treats simple predicates (including unification) always as a block.
Delayed goals are therefore woken only at the end of a successful
unification and/or a sequence of simple goals.
If a suspending variable is bound in a simple goal, the suspended
goals are woken only at the end of the last consecutive simple
goal or at the clause end.
If the clause contains simple goals at the beginning of its
body, they are considered part of the head (extended head)
and if a suspending variable is bound in the head unification or
in a simple predicate in the extended head, the corresponding
delayed goals are woken at the end of the extended head.
is also considered a simple goal and is therefore
always executed before waking any pending suspended goals.
This is important to know especially in the situations where the cut
acts like a guard, immediately after the clause neck or after
a sequence of simple goals.
If the goals woken by the head unification or by the extended head
are considered as constraints on the suspending variables,
the procedure will not behave as expected.
The idea here is that integers/2 fills a list with integers on demand,
i.e. whenever new list elements appear.
Filter/3 is a predicate that removes all integers that are a multiple
of P. In the example query, the call to integers/2 initially delays.
When filter/3 is called, Ints gets instantiated in the head unification
of the second clause of filter/3, which will wake up integers/2. However,
since the second clause of filter/3 has an extended head which extends up to
the cut, integers/2 will not actually be executed until after the cut.
Therefore, N is not yet instantiated at the time of the arithmetic test
and causes an error message.
filter(_P,,) :- !.
N mod P =\= 0,
delay integers(_, List) if var(List).
integers(N, [N|Rest]) :-
N1 is N + 1,
?- integers(2, Ints), filter(2, Ints, [X1,X2]).
The reason why delayed goals are woken after the cut and not before
it is that neither of the two possibilities is always the intended
or the correct one, however when goals are woken before the cut,
there is no way to escape it and wake them after, and so if
a nondeterministic goal is woken, it is committed by this cut
which was most probably not intended.
On the other hand, it is always possible to force waking before the cut
by inserting a regular goal before it, for example true/0,
so the sequence
can be viewed as a special cut type.
As a consequence, the example can be fixed by inserting true at the
beginning of the second clause.
However, a preferable and more robust way is using the if-then-else
construct, which always forces waking suspended goals before
executing the condition.
This would also be more efficient by avoiding the creation of a choice point:
(N mod P =\= 0 ->
LL = [N|NLI],
filter(P, LI, NLI)
17.11.2 Cuts and Suspended Goals
relies on a fixed order of goal execution in that it discards
some choice points if all goals preceding it in the clause body have
If some of these goals delay without being woken before the cut,
or if the head unification of the
clause with the cut wakes any nondeterministic delayed goal,
the completeness of the resulting program is lost
and there is no clean way to save it as long as the cut is used.
The user is strongly discouraged to use non-local cuts together with
coroutining, or to be precisely aware of their scope.
The danger of a cut is twofold:
Delaying out of the scope of a cut:
a cut can be executed after some calls preceding it in the clause
(or children of these calls) delay. When they are then woken later,
they may cause the whole execution to fail instead of just the
guard before the cut.
- Delaying into the scope of a cut:
the head unification of a clause with cuts can wake delayed goals.
If they are nondeterministic, the cut in the body of the waking clause
will commit even the woken goals