DemoII system

USER MANUAL

by

Henning Christiansen - henning@ruc.dk
Davide Martinenghi - dm@ruc.dk

Roskilde University
Computer Science Dept.,
P.O. Box 260, DK-4000 Roskilde, Denmark

Copyright © Davide Martinenghi, Henning Christiansen 1998
All rights reserved.
Permission is granted for copying and printing this document in single copies; further distribution is not allowed.

This document is part of the DemoII system, available at http://www.dat.ruc.dk/software/demo.html.


Contents

  1.   Introduction
    1. Status of the system and documentation
    2. Background reading
  2.   Installing and starting the DemoII system
  3.   An extended syntax for names of object language phrases
    1. Using the extended syntax with Prolog
    2. Meta-variables
    3. Naming operators of programs, clauses, formulas, and terms
    4. Summary of the object language syntax
  4.   An annotated example
    1. The program file
    2. Using demo
  5.   Declaring your own object program modules
  6.   Constraints provided by the DemoII system
    1. Available type and instance constraints
    2. Constraints for negation
    3. Other constraints
    4. Dynamic properties of the constraints
  7.   Getting rid of delayed constraints in the answers from DemoII
  8.   On control and potential loops in DemoII
    1. The basic control pattern in DemoII
    2. A good practical reason for viewing programs as sets

1   Introduction

DemoII is a meta-logic programming system that covers a wide range of automated reasoning tasks including abduction and induction. These task are specified in terms of declarative meta-level statements using the proof predicate demo, which can be specified as follows.

demo(<program>, <query>) iff <program> and <query> are names of object program P and query Q and there exists a substitution sigma with P provability Q sigma

P and Q refer to an object language, which we call O, of definite clause programs; by a name of an O-phrase (O-program, O-formula, etc.) we understand a unique ground term which identifies it.

DemoII provides an implementation of demo which is reversible in the sense that it works equally well for executing known programs as well as for generating new programs that make given goals provable.

Specifications in DemoII may include also syntactic bias and integrity constraints. The system appears as a highly generic environment for experiments with different reasoning methods. Distinct for the DemoII system is the ease with which different methods can be combined, e.g., integrating abduction and induction with default rules.

These properties are obtained using constraint logic techniques implemented with the Constraint Handling Rules library included in SICStus Prolog 3.7, under which the system runs. Among the features are:

In order to generate useful programs in this way, one often needs to define a meta-program which determines such class of useful programs. The DemoII package provides tools for writing such meta-programs, so you can specify your own predicate, say, useful, and use it in conjunction with demo, e.g.:

useful(<program>), demo(<program>, <facts>)

The use of co-routines makes it possible to achieve a reasonable execution speed by interleaving the actions of the two predicates.

The applications which you will find in the Examples directory of this package demonstrate the approach for tasks such as

The DemoII system provides an extended notation (see chapter 3) with explicit naming operators, which makes programming about the ground representation much more attractive. If you write, say,

\\ p(a, 'X')

this stands for the ground term that represents the O-atom p(a, X). As a programmer, you do not need to be aware of the fact that \\ p(a, 'X') is internally represented as the following structure:

gr(atom,p,[gr(constant,a,[]),gr(variable,'X',[])])

We recommend you to experiment with the example files before you build your own applications (e.g. the annotated example in chapter 4 below).

However, the authors' own experience is, despite the extended syntax and the system's declarative style, that this is a new style of meta-programming that one has to learn.


1.1   Status of the system and documentation

The DemoII system is a research product and there is no guarantee against errors, although the authors have done what they could to prevent those. DemoII is a successor of the previous Demo system (dating from around 1994) which is still available. The new system includes a better treatment of negation, a user-extendible syntax for the object language, and more clean implementation made using Constraint Handling Rules (CHR) which have been introduced in SICStus Prolog with the 3.7 release, autumn 1998. DemoII is slower than its predecessor which we partly refer to the current implementation of CHR and which may change with future releases. However, the increase of processor speed has fully compensated for this.

This user manual shows only the general ideas of the system in order to get acquainted with it; more technical details can be found in the reference manual and the examples files provided with the package.

The documentation included in the system consists of

For problems, questions, complaints, suggestions or perhaps applications of the DemoII system which would fit into the Examples directory, do not hesitate to write to henning@ruc.dk (Henning Christiansen).


1.2   Background reading

The ideas behind the DemoII system and references to related work are described in a number of papers:

For a general introduction to meta-programming in logic we recommend two survey papers:

and the book

As the DemoII system runs under SICStus Prolog you may occasionally need:


2   Installing and starting the DemoII system

The demoII system can only be used in an enviromnent with SICStus Prolog (version 3.7 or later) installed and the reader is assumed to be familiar with its use.

To install the system, you need to:

  1. download the DemoII archive from the DemoII web page;
  2. extract all the files from the DemoII archive using folder names (that is keeping the directory structure used in the archive).

A directory called DemoII will then be created inside the directory from which you unpacked the archive. For instance, if in a Windows-based environment you used the root directory of drive C, you will have a directory C:\DemoII containing the following things:

Once the system is installed, it can be started by running SICStus from the DemoII directory, presumably by typing commands like the following ones:

C:\>cd DemoII
C:\DemoII>prolog
and then, inside SICStus, consulting the DemoII main file:
| ?- [demo].
(or one of the three others: demo_i, demo_o, demo_oi).

The system will confirm it has been loaded correctly and the following messages will appear on the screen:

{The extended syntax is ready}
{DemoII is ready}

To leave the system and Prolog, write as usual

| ?- halt.

3   An extended syntax for names of object language phrases

The DemoII system provides an extended notation with explicit naming operators to indicate ground names of object languages phrases in a compact form.

It is implemented by pre- and post-processors which

  1. read user queries and program files, and translate the extended syntax into Prolog terms before invoking Prolog,
  2. translate answers back into the extended syntax whenever possible before printing out.

The actual syntax is defined by a set of declarations provided by a part of the system that most users of DemoII do not have to care about - and which is not described in the present User Manual. It is possible for a user to extend and modify the syntax. A complete description of the extended syntax is found in the reference manual.

In the following, we describe the basic syntax coming with the DemoII package and which is sufficient for all examples provided with the package.


3.1   Using the extended syntax with Prolog

The term \\ p(a, 'X') is a way of writing the ground representation which serves as the name of the object language atom p(a, X). Let us analyze what happens with the following query:
| ?- Z = \\ p(a, 'X').

Z = \\p(a,'X') ?

yes

It might seem that everything is as usual, but if we insert a call to writeq in the query, we can see where the difference is.

| ?- Z = \\ p(a, 'X'), nl, writeq(Z), nl.

gr(atom,p,[gr(constant,a,[]),gr(variable,'X',[])])

Z = \\p(a,'X') ?

yes

Thus \\ p(a, 'X') is actually read as the ground term gr(...) printed out by writeq. The \\ operator is used here as a kind of quotation mechanism.


3.2   Meta-variables

An essential need in a meta-programming system is to be able to parameterize over parts of object language phrases. The ? operator is used for putting a meta-variable (i.e. a Prolog variable) inside a term name. So the expression \\ p(a, ?X) stands for the name of an object language atom whose predicate is p/2, whose first argument is the object language constant a and whose second component is unspecified, indicated by the meta-variable X.
| ?- Z = \\ p(a,?X),  nl, writeq(Z), nl.

gr(atom,p,[gr(constant,a,[]),_112])

Z = \\p(a,?X) ?

yes

3.3   Naming operators of programs, clauses, formulas, and terms

Three different naming operators are in use: By a formula we mean an atom, a constraint formed by =/2 or dif/2, the truth constant true, or a conjunction of other formulas. Paragraph 3.4 below gives a summary of the object language syntax.

Here are a few examples of names of object language phrases written using the extended notation. Notice that programs are written as list of clauses.

There are also forms which make it possible to write object language atoms and structures with unspecified predicate/function symbol and/or argument list; see paragraph 3.4 and the Reference Manual.

Below we will also show how to compose programs with the & operator and how to make user-defined object program modules.

When using the naming operators, it is sometimes needed to separate different operators with a space. In particular, none of the naming operators can:

In all these cases an extra space is needed. Examples:

Notice that, in the last case, if one omits the space, the Prolog interpreter will understand \\ as a functor whose arguments are a and b, and not as the naming operator \\. DemoII generates then a warning message.


3.4   Summary of the object language syntax

The following context-free grammar defines the syntax for the object language as it is expected to be written when preceded by one of the naming operators. Parentheses can be used (and even be needed sometimes) in the ususal Prolog fashion.
<program> ::=
[<clause>, ..., <clause>]
| <module-name>
| <program> & <program>
| []

Notice: An object program is a set of clauses and the clauses mentioned in the name of a program are expected to be different, even in programs composed by & and modules. The demo predicate and the module mechanism (below) will check that there are no duplicates and provide an error message when needed. In general, however, the system does not prevent the user from writing, say, \[p, p], thus creating a meta-level term which is not the name of a program.

<module-name> ::= <identifier>

<identifier> ::= a Prolog atom beginning with a small letter

<clause> ::= <atom> :- <formula>

<atom> ::=
<prolog_atom>(<term>, ...,<term>)
| <prolog_atom>

<prolog_atom> ::= a Prolog atom not beginning with '_' or a capital letter

<formula> ::=
true
| <atom>
| <constraint>
| <formula>, <formula>
| <formula>; <formula>

<constraint> ::=
<term> = <term>
| dif(<term>, <term>)

<term> ::=
<structure>
| <constant>
| <variable>

<structure> ::= <prolog_atom>(<term>, ..., <term>)

<constant> ::= a Prolog term matching atomic and not beginning with '_' or a capital letter

<variable> ::=
'_'
| a Prolog atom beginning with a capital letter

Inside a program, a clause of the form

<atom> :- true
can be abbreviated to
<atom>
but in all other contexts, it has to be written in the complete form.

Meta-variables preceded by ? can replace any sub-phrase, and its type will be deduced from the context.

If you wish to refer explicitly to the predicate (function) symbol and to the argument list, it is possible to use the following syntax for <atom>s or <structure>s:

<name> ^ <argument-list>
where
<name> ::= <prolog_atom>

<argument-list> ::=
[<term>, ..., <term>]
| []

This is especially useful when used together with meta-variables, e.g.

?F ^ ?AL

?P ^ [a,?X]

With this notation it is also possible to parameterize predicate and function symbols with an object variable. This is achieved by using an object variable as <name>. Example:

fails(Prog, \\ ('P' ^ ['X',yes], 'P' ^ ['X',no]))

Notice that the extended syntax provides another "expansion facility", a macro mechanism, that provides a simple way to define metalevel predicates defining classes of object programs. This is described in more details in the reference manual and applications of it are found in the example files.

The remaining details of the extended syntax can be found in the reference manual.


4   An annotated example

In the following we describe a very simple example developed in DemoII which is intended to serve as an introduction for using the system.

The example concerns abduction in a simple propopositional context without integrity constraints and, thus, covers only a small fraction of DemoII's capabilities. To get a better impression of what the system can do, the reader is referred to the other example files.


4.1   The program file

Assume the DemoII system is started as described above and we load the example file Examples/wet_grass.pl, which contains a very simple standard example of abduction, in the usual way:
| ?- ['Examples/wet_grass.pl'].

The file defines, among other things, an object program module in the following way:

object_module( garden,
\[ (grass_is_wet:- rained_last_night),
(grass_is_wet:- sprinkler_was_on)
]).

Details about the object_module directive are found in chapter 5.

The effect of the module declaration above is that you can use the constant garden anywhere an object program is expected, instead of writing all the clauses each time. I.e., the expression

\garden

is now a program name.


4.2   Using demo

We can use demo to ask queries to the garden program defined above. The program argument to demo is composed by & from the garden program and another program which consists of one unspecified clause, indicated by the meta-variable WHY (notice the use of the ? operator). See also the comments on & in paragraph 8.2.
| ?- demo( \(garden & [?WHY]), \\grass_is_wet ).

WHY = \ (rained_last_night:-true) ?

The answer is a value for WHY which is the name of a clause which makes the conclusion in the demo query provable. The actual answer we got seems to be a perfect abducible explanation.

Now, let's type ; for alternative answers: alas the interpreter runs into a loop! There is a good reason for this which we will discuss in chapter 8.

However, the main intuitive reason to these troubles is that our query to demo was not stated carefully enough. The query asked for any kind of clause which makes grass_is_wet hold. So we were actually very lucky that the internal control inside demo actually produced a sensible first answer!

Usually abduction is understood as the task of finding facts of a certain "primitive" kind.

To this end, our example file Examples/wet_grass.pl defines a meta-predicate abducible(_) as follows:


:- block abducible(-).

abducible( \ (rained_last_night:- true) ).    
abducible( \ (sprinkler_was_on:- true) ).
abducible( \ (full_moon:- true) ).
abducible( \ (sun_shines:- true) ).
abducible( \ (dog_on_lawn:- true) ).

The block declaration causes any call abducible(X) to delay until some other event (e.g., an internal action of demo) assigns a value to X. (Block declarations are described in the SICStus Prolog User's Manual).

Consider, now, the following query:


| ?- abducible(WHY), demo( \(garden & [?WHY]), \\grass_is_wet ).

WHY = \ (rained_last_night:-true) ? ;

WHY = \ (sprinkler_was_on:-true) ? ;

no

This looks more like abduction. The call abducible(WHY) delays a condition on WHY, which wakes up when demo actually begins to involve the "clause" WHY in a proof. In this tiny example, the block declaration does not actually make much difference but when things become more complicated it can be essential to have such additional conditions delay as long as possible. In paragraph 6.2 and in chapter 8 we discuss more aspects of the use of the blocking mechanism.

This ends the first little annotated example intended as an introduction to the basic functionality of the DemoII system. Other less trivial examples are found in the Examples directory.


5   Declaring your own object program modules

The DemoII system provides a module concept which can be used to attach a name to a collection of object languages clauses.

An object program module is defined by a directive as follows:

object_module(<identifier>, <program-name>)

<identifier> is, as already stated, a Prolog atom beginning with a small letter; <program-name> is any term which can be accepted as the name of an object program that does not contain meta-variables. Here the extended syntax provided by the \ naming operator is useful.

Example:

object_module( pq_program,

	\[ (p('X'):- q('X')),
	p(a),
	p(b)
	]).

The execution of this directive makes pq_program available as a program module which can be used as (part of) a program name, e.g.:

| ?- demo(\pq_program, ...).

In the translation from extended syntax to Prolog structures, a name like \pq_program is replaced by a copy of the original source text (unlike the previous version of the system, there is no preprocessing of clauses).

The ground representation of a module is stored as an asserted fact object_module_source/2.

You can also use the & syntax in the definition of a module, e.g.:


object_module( extended_pq_program,

       \ ( pq_program & [(q('X'):- r('X')), r(c)] )).

In this way you can build a hierarchy of object language programs which are extensions of each others.

Restrictions:

6   Constraints provided by the DemoII system

The demo predicate is defined by means of constraints. These constraints are, among other things, a convenient tool for dealing with user-defined meta-predicates to be used together with demo. For some applications it may be necessary to write another demo predicate which uses, say, another search strategy; the sample file Examples/linear_drinks.pl shows an alternative demo predicate which can produce programs under linear-logic-like conditions.

In this chapter we explain which constraints are available and what one may need to know about their execution behavior.

The kinds of constraints DemoII is working with are thoroughly described in the reference manual; in the following paragraphs we introduce type and instance constraints, constraints for negation and some auxiliaries. The other constraints are not described here, as they ought to be used only by DemoII internal control.

As a general introduction to constraint techniques in logic programming, we advocate the following:


6.1   Available type and instance constraints

There is one type constraint for every type defined in the object language:

Each constraint is satisfied whenever its argument is the name of an object phrase of the indicated type. Thus, for example, the following constraints are all satisfied:

Notice that each constraint name ends with an underscore in order to have a consistent way to distinguish them from a few Prolog built-ins.

Instance constraints are of the form

instance_(<Object>1, <Object>2)

An instance constraint is satisfied whenever <Object>1 and <Object>2 are names of object language phrases O1 and O2 with O2 being an instance of O1.

Instance constraints are essential for defining a meta-interpreter such as demo, but users who stick to the delivered demo predicate will probably never need to use instance constraints.


6.2   Constraints for negation

In order to treat conditions about non-provability at the object level, we implemented a constraint specified as follows:

fails(<program>, <query>)

iff <program> and <query> are names of object program P and query Q and there is no substitution sigma for which

P provability Q sigma

In the actual implementation, a goal like

fails(P, \\p(a))

will delay until P gets instantiated. This resembles an incrementally executing integrity constraint that delays subcomputations on remaining meta-variables and wakes up when these in turn are instantiated:

fails works internally with some other constraints, described in the reference manual. Such constraints can appear in the answer if the computation is delayed.


6.3   Other constraints

DemoII includes a few other constraints of which ground_ is probably the only one that is relevant when developing new applications in DemoII. We mention them here because they can appear as part of answers produced by the system.


6.4   Dynamic properties of the constraints

The reason why the system predicates above are referred to as constraints is their execution behavior. For example, a constraint
program_(P)
will not generate on backtracking all possible object program names. Rather, it delays a test which will wake up when some event occurs (unification to P or another constraint call) which might possibly violate program_(P). The implementation of this is mainly based on the Constraint Handling Rules library (CHR) included in the SICStus Prolog package (in a few cases we used SICStus block declarations instead).

Ideally, the user should not need to be aware of when or how constraints actually execute. However, user-defined meta-predicates may need to delay, thus functioning as a sort of constraints that interact with those set up by the demo predicate.

Whenever a constraint "decides" to assign a value to a variable X, the event also triggers user-defined predicates. Or, the other way around; a simple unification made by user code may trigger a large collection of accumulated system constraints.

A precise and formal model for execution of constraints is given in the paper "Automated reasoning with a constraint-based metainterpreter" referenced in the introduction. Here we present the basic rules in an informal way, which we believe is sufficient for those who want to develop their own meta-predicates:

  1. Type constraints (e.g. program_(P)) delay as long as possible.
  2. Whenever a constraint (type or instance) is set up on a variable, it will be checked for satisfiability against other possible constraints which concern that variable.
  3. The information about type and instance propagates to subcomponents of an object phrase wherever possible.

Rule (a) indicates that even a constraint such as atom_(A) does not assign a value to A. We found that this principle works better together with delayed user predicates.

According to rule (b), it happens for example that:

An example of application of rule (c) is the following:

| ?- clause_(\ (?A:- ?B)).

atom_(A),
formula_(B) ?

As an advice, we can give a rule of thumb saying that user-defined meta-predicates to be used in conjunction with demo should be as "lazy" as possible, i.e., have as many (sensible) block declarations as possible. The demo predicate will accumulate roughly one type constraint for each uninstantiated meta-variable. When or if such a variable is assigned a value, the constraint evaluates as a test. Type constraints do not assign values to variables; hence, they do not trigger delayed user predicates. Actually, this is true except for a few cases discussed in the reference manual. As a programmer or DemoII user, you seldom have to worry about type constraints.

Warnings:

In the normal execution mode, there is no occur check involved in solving instance constraints; see chapter 2 for how to enable occur check. In the examples provided with the the DemoII package this does not make a difference, but in another (yet undocumented) experiment trying to generate polymorphic type declarations from program text, the occur check appears to be essential.

There is no facility in the DemoII system which prevents the user from writing additional code which delays calls of his or her own predicates which are unsatisfiable in conjunction with the accumulated system constraints.


7   Getting rid of delayed constraints in the answers from DemoII

Assume a meta-variable X appearing in an argument to demo. When the demo call has succeded, the answer printed out by the system often consists of a collection of more or less comprehensible constraints about X. Alternatively, X may be assigned a structure with a number of new meta-variables on which quite many constraints can be expected.

To provide more compact answers we have implemented a predicate

close_constraints(<AnyPhrase>)

which localizes the uninstantiated meta-variables in the argument <AnyPhrase> and tries to assign values to them which will satisfy the accumulated constraints. The sample file Examples/still_life.pl shows how it works.

The close_constraints predicate is a purely heuristic construction which succeeds only once giving a suggestion for a prototypical, concrete answer contained in the constraints. It applies an adapted (an not yet documented) least-general-generalization method, and includes strategies for reusing existing symbols and inventing new ones when necessary.

Sometimes the set of accumulated constraints and delayed user predicates is unsatisfiable. In this case the predicate issues a warning and fails, hoping for demo and the user predicates to come up with a better set of constraints.

The following example shows a problem that can arise with close_constraints. Consider the query:

demo( \ (... ?X ...), ...),
close_constraints(X),
condition(X).

It may be the case that this query fails because close_constraints instantiates the solution provided by demo in a way different from what you expected. This query may fail even if demo has produced a set of constraints that contain a grounded solution satisfied by condition(X). The correct action is likely to reprogram condition(X) as a co-routine that interleaves with the actions of demo.

The close_constraints predicate is especially useful in the early experimental phases in the development of a new application of demo. The first version of a new meta-predicate to support demo is typically not precise enough: it can be quite a challenge to formalize what it means for an object program to be a good object program in a given context. So in your first experimental execution of

good(P), demo(P, ...),
the concrete information you get about P is probably very little and the amount of delayed calls quite difficult to understand. Try instead
good(P), demo(P, ...), close_constraints(P)
and you will get a single prototypical representative for the (typically infinite) set of solutions contained in the set of constraints. This will quite often indicate the points where the good predicate needs revision.

The more you revise the good predicate, the less meta-variables are left for close_constraints and, in the final version, close_constraints can be dropped or used only for trivial tasks such as closing open program tails.

We have found that the warning issued by close_constraints on failure is an effective means for recognition of programming errors in user predicates (but, alas, not for localizing the erroneous point!).


8   On control and potential loops in DemoII

We cannot present a methodological textbook on meta-programming using the DemoII system; here we discuss a few points which may be of importance for those who want to develop new applications.


8.1   The basic control pattern in DemoII

The demo predicate works top-down, trying (and inventing, if necessary) clauses in the order they appear in the program and executing goals from left to right (in other words, in the same manner as a traditional Prolog system). As in Prolog, this may result in loops and other unwanted behavior unless the programmer has taken proper precautions. And when demo is applied for generating programs, there are of course even more pitfalls. Without any additional meta-programs supplied for the particular application, demo will easily get into trouble.

The following basic version of the demo predicate illustrates this control pattern.

demo(P, Q) :-
	program_(P),
	no_duplicates_(P),
	instance_(Q, Q1),
	demo1(P, Q1).

demo1(_, \\true).

demo1(P, A):-
	member_( C, P),
	instance_( C, \ (?A :- ?B1)),
	demo1(P, B1).

demo1(_, \\ (?T1 = ?T2)) :- T1 = T2.

demo1(_, \\ dif(?T1, ?T2)) :- dif(T1, T2).

demo1(P, \\ (? A, ?B)) :- demo1(P, A), demo1(P, B).

demo1(P, \\ (? A; ?B)) :- demo1(P, A); demo1(P, B).

We will illustrate the potential problems with the following query to demo for object programs in which the object query p is true.

| ?- demo(X, \\p).

The first answer is as follows (note that the extended syntax is used when the answer is printed out):

X = \[(p:-true)|?_A],
user:not_member_(\ (p:-true),_A),
user:no_duplicates_(_A),
program_(_A) ?

This says that X is a program having a fact saying "p holds" and some internal constraints expressing that the tail of X must be a program.

When asking for a second solution, we run into a loop which is very easy to explain. Asked for an alternative answer, demo will try to redo its last choice, which was to expand the tail of the novel clause to true. Alternatively, it tries to expand it to an atom, and this atom must unify with the head of a clause in the program; as expected it tries the first clause (whose head is p), so the object clause p:- p is created.

However, the observed behavior only reflects the quality of the query. It is difficult to imagine an application where one is interested in just a program which makes p provable: there will be many weird ones, also which include the clause p:- p. In most cases there will be some inherent criteria, formalized, say, as a meta-predicate good(_), which will exclude this. This was the case of the abduction example in file Examples/wet_grass.pl which we discussed in chapter 4 above.


8.2   A good practical reason for viewing programs as sets

An object program is a set of clauses and the name of a program is a meta-level term, a list-like structure which has one element for each clause (its name).

This means that no duplicates are allowed at the representation level. In practice, this makes the demo predicate less prone to loops. Whenever demo needs to expand (using member_) a variable in the position of a program tail into a structure with one new clause and yet a new tail, it will not try to expand this new tail again on backtracking. Consider an example query,

conditions(P), demo( P, \\p)

and assume that demo expands P to \ [?C1 | ?P1] and for some reason (e.g., due to conditions(P)) this fails, i.e. there is no such clause which can make p provable. Without the mentioned mechanism, demo might now try to expand P1 into \ [?C2 | ?P2], which is likely also to fail; and so on.

With demo as we have implemented it, the whole demo call fails after the first failed expansion.

This principle, expand-tail-only-once-on-failure, extends to programs composed by the & operator. We illustrate this by the following query:

red(Pr), green(Pg), blue(Pb),
demo( \ (?Pr & ?Pg & ?Pb), \\p).

The predicates red, green and blue define different kinds of clauses which are relevant with respect to the given problem domain. Now, if the call to demo (or perhaps some deeply nested, recursive call) fails on expanding the tail (of a tail of ...) of Pr (i.e., the red meta-condition fails), then expansion of Pg is tried, and if this also fails (i.e., green fails), expansion of Pb is tried (and then tested with blue).

As the example indicates this makes it easier to structure meta-predicates in a more orthogonal way.

This document was last modified on Monday, December 21, 1998