The *propia* and *chr* libraries are implemented using a set
of underlying facilities in ECLiPSe which support data-driven computation.
The main feature supporting data-driven computation is the *
suspension*.
This is illustrated below.

[eclipse 1]: lib(fd). * fd loaded [eclipse 2]: suspend(writeln("Wake up!"),1,X->inst), writeln("Do this first"), X=1. * Do this first * Wake up! * X = 1 * yes. [eclipse 3]: suspend(writeln("Wake up!"),1,X->inst), current_suspension(S), suspension_to_goal(S,Goal,M), kill_suspension(S), call(Goal,M). * Wake up! * ... * yes. [eclipse 4]: X::1..10, suspend(writeln("Wake up!"),1,X->min), X#>3, * Wake up! * X = X{[4..10]} * yes.Handling Suspensions

A suspension is a goal that waits to be executed until a certain event occurs. Each suspension is associated with a set of variables, and as soon as a relevant event occurs to any one of the variables in the set, the suspension ``wakes up'' and the goal is activated. One such event is instantiation: all the suspensions on a variable wake up when the variable is instantiated.

In figure the first query loads the *fd*
library, which will be used in
the last example.
It is preferable to load all the libraries that may be needed at the
start of the session.

Query 2 suspends a goal *writeln(``Wake up!'')* on one variable
*X*. The goal will be executed as soon as *X* becomes instantiated
( ). When woken the goal will be scheduled with a
certain priority. The priority is given as the second argument of *
suspend*. In this case the priority is 1, which is the highest
priority.
The remainder of query 2 performs another write statement and then
instantiates *X*.
The output from ECLiPSe shows that the suspended goal is not executed,
until *X* is instantiated, after the system has already output *Do
this first*.

Query 3 shows various facilities for explicitly handling a suspension. The current suspensions can be accessed. (It is also possible to access the just the suspensions on a particular variable.) A suspension can be converted to a goal. A suspension can be ``killed'', so it is no longer accessible or wakeable. The suspension has no connection to the goal, however, which can still be executed.

To save space the output of variable values is omitted here.

Finally query illustrates another kind of event that can wake up a
suspended goal.
In this case the goal is suspended until the lower bound of the finite
domain associated with *X* is tightened ( ).

There are other events which can wake suspended goals associated with
other constraint handlers, but the most general event is that the
variable becomes more constrained in *any* way (expressed as ).
Goals suspended in this way will wake when any new constraint on *X*
is added (an *fd* constraint, a *ria* constraint, or an *
eplex*
constraint.

Finally it is also possible to retrieve goals suspended on a given variable, or those associated with a given event on a given variable.

Based on this simple idea it is possible to define a constraint
behaviour explicitly.
As a simple example let us make a constraint that two variable differ
by more than some input number *N*.
We will call the constraint *ndiff(N,X,Y)*, where *N* is the
difference, and *X* and *Y* the two variables.
Its behaviour will be to tighten the finite domains of the variables.

In figure we implement a behaviour for our:- lib(fd). :- suspend. ndiff(N,X,Y) :- mindomain(X,XMin), maxdomain(Y,YMax), YMax<XMin+N, !, X#>=Y+N. ndiff(N,X,Y) :- mindomain(Y,YMin), maxdomain(X,XMax), XMax<YMin+N, !, Y#>=X+N. ndiff(N,X,Y) :- suspend(ndiff(N,X,Y),3,[X,Y] -> any).Using Suspensions to Implement Constraints

The first clause for *ndiff* checks if the lower bound for *X* is
so close to the upper bound for *Y*, that *X* cannot be less than *Y*
(if it was, then to satisfy the *ndiff* constraint we would need to
have *Y*>=*X*+*N*).
In this case it imposes the constraint that .

The second clause does the symmetrical test on the lower bound of *Y*
and the upper bound of *X*.

If neither of these conditions are satisfied, then *ndiff* doesn't
do anything.
It just suspends itself until the finite domains of *X* or *Y* are
tightened ([*X*,*Y*] -> *any*).

This very same mechanism of suspended goals is used to implement all
the built-in constraints of ECLiPSe.
For example the constraint is implemented using a
goal which is suspended on two events:
a change in the maximum of the domain of *X*,
and a change in the minimum of the domain of *Y*.
Typically all the finite domain built-in constraints are suspended on
events which occur to the finite domains of their variables.

Before concluding this subsection, we should observe that the
different constraint libraries of ECLiPSe are supported by a very
flexible facility.
The information about each kind of constraint on a variable is held in
a data structure which is attached to the variable called an
*attribute*.
When the *fd* library is loaded, each variable in ECLiPSe has a
finite domain attribute.
If the variable has no finite domain, this attribute contains nothing,
and the behaviour of the variable is just as if it had no attribute.
On the other hand if the variable does have a finite domain, then the
attribute stores the finite domain, as well as pointers to all the
suspended goals which are waiting for an event to occur to the finite
domain.

Naturally *ria* constraints and *eplex* constraints are stored
in other attributes, and they have their own suspended goals attached
to them.

Any ECLiPSe user can define and implement a completely new constraint handling library in three steps.

- A new attribute storing information about the new class of constraints, must be defined.
- Events specific to this class of constraints must be specified.
- New constraint behaviours must be implemented in terms of goals which suspend themselves on these events.

Wed Sep 3 18:07:19 BST 1997