The facilities presented in this section are largely superseded by the more general mechanism described earlier in this chapter. However they are still supported and the information given here may give useful hints for porting or improving existing code.
For delaying calls to user-defined Prolog predicates, ECLiPSe provides delay clauses. Delay clauses are a declarative means (they are in fact meta-clauses) to specify the conditions under which the predicate should delay. The semantics of delay clauses is thus cleaner than many alternative approaches to delay primitives.
A delay clause is very similar to a normal Prolog clause. It has the form
delay <Head> if <Body>.A Prolog predicate may have one or more delay clauses. They have to be textually before and consecutive with the normal clauses of the predicate they belong to. The simplest example for a delay clause is one that checks if a variable is instantiated:
delay report_binding(X) if var(X). report_binding(X) :- printf("Variable has been bound to %w\n", [X]).
The operational semantics of the delay clauses is as follows: when a procedure with some delay clauses is called, then the delay clauses are executed before executing the procedure itself. If one of the delay clauses succeeds, the call is suspended, otherwise they are all tried in sequence and, if all delay clauses fail, the procedure is executed as usual.
The mechanism of executing a delay clause is similar to normal Prolog clauses with two exceptions:
delay p(a, X) if var(X).does not match the call p(A, b) but it matches the goal p(a, b).
If the matching succeeds, the body of the delay clause is executed. If all the body subgoals succeed, the call is suspended. Otherwise, or if the head matching fails, the next delay clause is tried and if there is none, the call continues normally without suspending.
The form of the head of a delay clause is not restricted. For the body, the following conditions hold:
CAUTION: when the symbol :- is used instead of if in the delay clause, the resulting term is a clause of the procedure delay/1. To ease the detection of such mistakes, the procedure delay/1 is protected and so an exception is raised.
delay integer_list(L) if var(L). delay integer_list([X|_]) if var(X). integer_list([]). integer_list([X|T]) :- integer(X), integer_list(T).
delay p(X, X, Y) if var(Y).
delay p(X) if compound(X), arg(1, X, Y), nonground(Y).
delay p(X) if nonground(2, X).
\
==/2
predicate as a delaying condition is useful mainly
in predicates like X + Y = Z which need not be delayed if X == Z.
Y can be directly bound to 0, provided that X is later bound to a number
(or it is not bound at all)
The condition X \
== Y makes sense
only if X or Y are nonground: a delay clausedelay p(X, Y) if X \== Y.executed with the call ?- p(a, b) of course succeeds and the call delays forever, since no variable binding can wake it.
~
/1
can be in ECLiPSe simply implemented asdelay ~ X if nonground(X). ~ X :- \+ X .MU-Prolog's wait declarations can be in most cases simulated using delay clauses. Although it is not possible to convert all wait declarations to delay clauses, in the real life examples this can usually be achieved. The block declarations of SICStus Prolog can be easily expressed as delay clauses with var/1 and nonground/1 conditions. The freeze/2 predicate (e.g. from SICStus Prolog, same as geler/2 in Prolog-II) can be expressed as
delay freeze(X, _) if var(X). freeze(_, Goal) :- call(Goal).The transcription of when declarations from NU_Prolog basically involves negating them: for instance, the when declarations
?- flatten([], _) when ever. ?- flatten(A._, _) when A.can be rewritten as
delay flatten(A, _) if var(A). delay flatten([A|_], _) if var(A).Note however, that in contrast to when declarations, there are no syntactic restrictions on the head of a delay clause, in particular it can contain any compound terms and repeated variables.
The semantics of the delay clauses is also clearer than it is the case for other comparable constructs - by defining when the call has to delay the user naturally expresses the necessary condition. If the user specifies when the call should not be delayed, this condition is no longer quite straightforward - if there is no condition or if the condition does not match the call it would mean that the call should wait forever, which is certainly not the intended semantics.