.nr hm 2v               \" space bwn top of page and header
.nr tm 4v           \" space bwn top of page and text
.he '''User Manual'  \" header - title
.fo ''%''               \" footer - page number
.po 1.25i	    \" left margin
.ll 6.0i	    \" line length
.tp 
.de c
.nr _F \\n(.f
.ul 0
.ft C
.if \\n(.$ \&\\$1\f\\n(_F\\$2
.rr _F
..
.sp 2i
.(l C
.sz +6
\fBTHE PARALLEL PARLOG USER MANUAL\fR
.sz -4
.sp 0.5i
Jim Crammond, Andrew Davison, Alastair Burt,
Matthew Huntbach and Melissa Lam
.sp 0.5v
Parlog Group
Department of Computing
Imperial College, London
.sp 0.5v
9th October 1989
.sp
Revised
14th June 1990
.)l
.bp

.sh 1 "INTRODUCTION"

The first part of this document is a user manual in tutorial
form, and may be read while trying
out the examples at a terminal.  
The second part is a reference manual, and contains a definition of
Parallel Parlog and documentation on system predicates.
The reader is assumed to have some
knowledge of Parlog and Unix\(tm.

.sh 1 "ABOUT PARALLEL PARLOG"

Parallel Parlog is an implementation of Parlog for shared memory
multiprocessor machines. Versions currently exist for the Sequent Balance and
Symmetry, Encore Multimax and Alliant FX/8. Uniprocessor versions are also
available for Sun3 and Sun4 workstations. The syntax is standard Parlog and 
is compatible with the SPM [Foster et al. 1986]
and MacParlog [Conlon and Gregory 1989]

The language is as defined in [Gregory 1987],
except for the \fInonflat guard
restriction\fR [see 9. Guard Safety Considerations], the restricted form of the metacall
[see section 6.2. in the Reference Manual], and the introduction of a simple module
scheme [see 8. Modules].

This manual describes the second release of the Parallel Parlog system,
.i "V1.4" ,
which contains a number of improvements over the first release
.i "V1.0" .
Any program compiled on the first release will need to be recompiled in
order to be executed on the new release of the system.

The main feature of the second release of the Parallel Parlog system
is a new memory management system that allows the different data areas
within the emulator [Crammond 1990] to dynamically expand (and contract) as
required. On machines that allow shared memory to be expanded during execution
this makes the new Parlog system somewhat more robust, particularly when using
many processors, and also more economical when executing small programs,
particularly on a single processor.

.sh 1 "ENTERING AND EXITING THE PARALLEL PARLOG SYSTEM"

The system can be entered by typing the command
.c parlog .
This command has one option \fC-n\fIx\fR which specifies
that the system should execute on \fIx\fR processors.
On startup, the Parlog
system displays the following message on the screen:

.(l
.ps -1
.vs -1
.c
Parallel Parlog V1.4 - 12 Jun 1990
Copyright (C) 1990, Parlog Group, Imperial College

|\ ?-
.r
.)l

To exit Parlog, type the end of file character (usually control-D) or
the command
.c halt .

.sh 1 "RUNNING QUERIES"

In response to the \fC|\ ?- \fR prompt the user may enter any conjunction of
Parlog calls terminated by a full stop.  This conjunction is evaluated as a
query, and if it is successful the bindings for the query are printed out and the
word
.c yes
appears.  Bindings are not printed for void variables (denoted by an
underscore). If the query fails then this is indicated with
.c no .
This is illustrated by the following simple example:

.(l
.ps -1
.vs -1
.c
| ?- \f(CBX is 1 + 2.\fP
 X = 3

yes
| ?- \f(CB_ is 1 + 2.\fP

yes
| ?- \f(CBX is 1 + 2, X is 2 + 2.\fP

no
| ?- \f(CBhalt.\fP
ncalls/utime = 461 / 0.02 = 23050.0 RPS
.r
.)l

The evaluation of a query can be interrupted by typing the
interrupt character (usually control-C). This should produce the prompt
\fC"Parlog interruption ? "\fR.
By typing \fCy\fR<return> to this prompt, the execution of the query will
be aborted; typing \fCn\fR<return> will cause the execution to resume.

Note: if the interrupt prompt does not appear on the screen within a few
seconds of typing an interrupt then the
system has \(lqhung\(rq in some way. The only rescue from this 
situation is to type the
quit character (usually control-\e).

.sh 1 "LOADING AND COMPILING"

Users may define their own Parallel Parlog predicates by placing them in a
text file. The correct syntax for predicates is defined in
the Reference Manual [section 1].  To make them 
available within the system, a
Parallel Parlog object code file should be generated by using the
.c compile/1
predicate, and this should then be loaded with the
.c load/1
predicate.

It is usual for the Parlog text file to have a \fB.par\fR extension and
the object file an \fB.o\fR extension, as in the following example,
but this is optional [see section 5 in the Reference Manual].

.(l
.ps -1
.vs -1
.c
tutorial% \f(CBcat tst.par\fP

mode merge(?,?,^).
merge([A|As],Bs,[A|Cs])<-
    merge(As,Bs,Cs).
merge(As,[B|Bs],[B|Cs])<-
    merge(As,Bs,Cs).
merge([],Bs,Bs).
merge(As,[],As).

tutorial% \f(CBparlog\fP
Parallel Parlog V1.4 - 12 Jun 1990
Copyright (C) 1990, Parlog Group, Imperial College

| ?- \f(CBmerge([1,2],[3,4],X).\fP
[Warning: the procedure merge/3 is undefined]

no
| ?- \f(CBcompile(tst) & load(tst).\fP
[loading compiler]
Compiling file tst.par
merge / 3
File tst compiled.

yes
| ?- \f(CBmerge([1,2],[3,4],X).\fP
 X = [1,2,3,4]

yes
| ?- \f(CBhalt.\fP

ncalls/utime = 5269 / 2.18 = 2416.97 RPS
tutorial% \f(CBls\fP
tst.o		tst.par\fP
tutorial%
.)l

Terms in the source file with the form \(lq\fC<- \fIconjunction-of-goals.\fR\(rq
are treated as commands, which are executed automatically as
the object file is loaded.

.sh 1 "TRACING"

The Parallel Parlog system supports rudimentary tracing facilities, through
the use of
.b spypoints.
A spypoint may be placed on a predicate using the 
.c spy/1 
predicate.
Thereafter, each time the predicate is called, the goal and the current
state of its arguments
is displayed on the screen preceded by a
.c "Call:"
message. The user is then prompted, and four kinds of response
are possible.

1)  When the user 
types <return>, the traced call is executed and a further message
is displayed on termination of the call. This message is either
.c "Exit:"
or
.c "Fail:" ,
followed by the goal and its arguments.

2)  Typing \fCn\fR<newline>
will cause the spypoint to be removed before executing the call.
Thereafter, further calls to the predicate will not be traced (but
existing calls will still be traced when they terminate).
Spypoints can also be removed with the
.c "nospy/1"
predicate.

3)  Typing \fCa\fR<newline> will abort the call.

4)  Typing \fCc\ goal.\fR<newline>
will cause
.c goal
to be executed. This can be useful for setting further spypoints
before proceeding with this call. Once
.c goal
has completed, the user is prompted again for action on this call.

.(l
.ps -1
.vs -1
.c
| ?- \f(CBspy(merge/3).\fP

yes
| ?- \f(CBmerge([1,2],[],[1,3]).\fP
Call: merge([1,2],[],[1,3]) ? 
Call: merge([2],[],[3]) ? 
Fail: merge([2],[],[3]) ? 
Fail: merge([1,2],[],[1,3]) ? 

no
| ?- \f(CBmerge([1,2],[],X).\fP
Call: merge([1,2],[],_3122) ? 
Call: merge([2],[],_3220) ? 
Call: merge([],[],_3274) ? 
Exit: merge([],[],[]) ? 
Exit: merge([2],[],[2]) ? 
Exit: merge([1,2],[],[1,2]) ? 
 X = [1,2]

yes
| ?- \f(CBmerge([1,2],[],X).\fP
Call: merge([1,2],[],_3894) ? \f(CBa\fP
 
no
| ?- \f(CBmerge([1,2],[],X).\fP
Call: merge([1,2],[],_3589) ? \f(CBn\fP
 X = [1,2]

yes
| ?-

.)l
.br
Notes:
.ip 1.
The emulator will continue to execute runnable goals while waiting for the
prompt to a traced call; thus potentially altering the scheduling behaviour,
particularly on one processor.
.ip
A consequence of this is that
parallel invocations of a spied predicate result in multiple
.c "Call:"
messages appearing, each requiring a response. This is rather
unsightly at times.

.ip 2.
A common fault that causes programs that work on one processor to
fail on multiple processors is the incorrect use of
.c "var(X)"
in a guard. Such a guard may succeed \(lqunexpectedly\(rq because it runs
in parallel with a goal that eventually binds X.
Remember,
.c "var(X)"
is time dependent and must be used with care in parallel programs.

.sh 1 "ERROR MESSAGES"

There are four sorts of error messages which the user may encounter :

.sh 2 "Builtin Errors"

When the system predicates are given incorrect
input they generate messages of the form :

\fB[Error:  \fImessage\fB: \fIbuiltin name and arguments\fB]\fR

.sh 2 "Syntax Errors"

The 
.c read 
predicates [see section 6.3.3. of the Reference Manual] return error messages
when they are used with input that does not conform to Parlog
syntax.  Since the compiler and the top level query
evaluator use these predicates, the user will find the same messages displayed
here.  The format is:

\fB**Syntax Error:\fI offending term\fR

The offending term is listed, and
the place where the error occurred is marked by
the word
.c "<<here>>" .

.sh 2 "Compiler Errors"

The compiler may generate the following messages :

\fB**Syntax Error:\fR - see above.

\fB**Mode Declaration Error:  \fIMode\fR - The mode is wrongly formatted;
usually a \fC^\fR or \fC?\fR is missing.

\fB**Compiler Error - Name/Arity mismatch:  \fIName\fB/\fIArity\fR - The
clauses grouped together as one procedure with the 
sequential search operator (\fC;\fR) do not
share the same name and arity.

\fB**Compiler Error: \fIName\fB/\fIArity\fR - Any other compiler error.

After processing the source file, a summary of all of the error
messages is displayed. This is illustrated by the example session
given below, which captures a syntax error in the first predicate
of the
.b "kkqueen.par"
file.

.(l
.ps -1
.vs -1
.c
tutorial% \f(CBcat kkqueen.par\fP

mode gen(?, ^).
gen(N, [N|Xs]) <-  
  N > 0 :
  N1 is N-1, 
  gen(N1, Xs).
gen(0, []). 
                            
mode append(?, ?, ^).
append([A|X], Y, [A|Z]) <- 
  append[X, Y, Z).        
append([], Y, Y).            

mode count(?, ?, ^).
count([], M, M).
count([X|Xs], M, Total) <- 
  M1 is M + 1 ,
  count(Xs, M1, Total).

tutorial% \f(CBparlog\fP
Parallel Parlog V1.4 - 12 Jun 1990
Copyright (C) 1990, Parlog Group, Imperial College

| ?- \f(CBcompile(kkqueen).\fP
[loading compiler]
Compiling file kkqueen.par
gen / 2

** Syntax Error: append([A|X],Y,[A|Z])<- append <<here>>[X,Y,Z).
append / 3
count / 3
File kkqueen partially compiled:
One or more syntax errors in or before:
append / 3
These clauses missing from the object code!

yes
| ?- \f(CBhalt.\fP

ncalls/utime = 6579 / 2.4 = 2741.25 RPS
tutorial%
.)l

.sh 2 "Deadlock"

When the evaluation of a query cannot terminate because there are calls
suspended on input matching constraints, the system prints out
all the suspended calls and then the message :
.(l
.ps -1
.c
[deadlock]
.)l

Note: goals which were suspended before a previous deadlock, or were
executing before an interrupt was typed, may be displayed among the list of
goals suspended during this deadlock.

.sh 1 "MODULES"

Parallel Parlog supports a simple module system to control the name space of
predicates. This is based on the notion of a
.b "current module",
which is the
place where a query currently looks for predicate definitions.
The predicate
.c current_module/1
returns the name of the current module, and
.c module/1
changes it [see section 7. in the Reference Manual].

When predicates are loaded into the system, they are placed in the
current module. Thus a command such as
\(lq\fC<- module exmpl.\fP\(rq at the top of the source file will cause
the predicates in the object file to be loaded into module
\fCexmpl\fP. However, after the load is finished, the
current module becomes
the module which was being used prior to the load.

By default, predicates loaded into a module are private to that module
and
can only be called by goals within that module. Predicates can be made
visible to all other modules by the
.b public/1
command, e.g.
.c "public a/1, b/2,\ c/3." ,
which means that it can
be invoked from any module. A public predicate is read-only in all
modules other than the one in which it is defined, so the definition of a
public predicate not in the current module cannot be replaced in subsequent
loads by an alternative (private) one.

Predicates can also be imported into modules without making them public to all
modules. This is a two stage process: firstly the predicates are made
exportable in the module in which they are defined with
.b export/1 ,
and secondly they are imported with
.b import/2 ,
from the module in which they are defined into the current module.
For example, suppose the current module is set to
.c foo ,
then
.c "export([a/1,b/2,c/3])"
makes predicates
.c a/1 ,
.c b/2
and
.c c/3
all exportable. Now in module
.c bar ,
the predicate
.c b/2
can be imported from
.c foo
using
.c "import(foo,[b/2])." .

There are two predefined modules:
.i system
and
.i user .
All Parlog systems predicates, as defined in the reference manual,
are defined in module
.i system
and are made public. In this way, the module mechanism
is transparent to the user who does not wish to use it.
The query evaluator executes by default in module
.i user
so that private predicates used by the system and compiler are
invisible to the user.

The binding of a call to a predicate in the
appropriate module is done at load time, thus incurring no overhead at
run time. However, the binding for interpreted calls, using
.c call/1 ,
is done at run time in the context of the current module. Thus, the predicate
must either be in the current module or imported into it. A predicate within
a given module can be called explicitly using 
the \fImodule\fC#\fIgoal\fR
syntax, e.g.

.(l
.ps -1
.c
| ?- \f(CBsystem#assemble(file).\fP
.)l

.sh 1 "GUARD SAFETY CONSIDERATIONS"

The usual \fBguard safety\fR restrictions apply, which mean that bindings
of variables in a guard are not made visible to the body of the clause
until the guard has committed.

In addition, Parallel Prolog has a \fBnonflat guard
restriction\fR which means that only 
one clause can be tried once 
a conjunction of nonflat clauses has been encountered.  
A nonflat clause is one with a nonflat guard, which
generally means a user-defined guard predicate or
conjunction of predicates. System-defined flat
predicates include all unification primitives (except
.c ground/1
and
.c "\e==/2" ),
all type tests, 
.c is/2, 
term comparison and arithmetic comparison.
More details on flat and non-flat guards can be found in [Gregory 1987].
 
The consequence of this nonflat guard 
restriction is that the programmer should ensure
that all clauses containing nonflat guards (except the last) should
end with a sequential clause separator \(lq\fC;\fR\(rq.
This coding strategy will ensure that
a candidate nonflat clause will not be
tried in parallel with other candidates.

With this restriction, the if-then-else form of deep guard is still possible
(as seen in examples 1 and 2),
but the or-parallel search technique - two alternative guards executing in
parallel - is not (example 3).

.(l
.ps -1
.vs -1
.ft CB
\fR(1)\fP mode p1(?,^).
   p1(X,S) <- try(X) : S=ok ;
   p1(X,S) <- S=failed.

\fR(2)\fP mode p2(?,?).
   p2([A|B],Y) <- g1(A), g2(A) : b1(B,Y);
   p2(X,[A|B]) <- g1(A), g2(A) : b2(B,X);
   p2(X,Y)     <- b3(X,Y).

\fR(3)\fP % this is not allowed:
   mode p3(?,?,?,^).
   p3(X,L,R,T) <- find(X,L,T1) : T=T1.
   p3(X,L,R,T) <- find(X,R,T2) : T=T2.
.)l

There are transformations that
automatically convert general Parlog into the restricted form, and future 
releases may perform these during compilation.  At present, the 
user is responsible for any alterations to his code.

.he '''Reference Manual'   \" header for Reference Manual
.bp
.uh "REFERENCE MANUAL"

.sh 1 "SYNTAX" 1 1 1 1 1 1

A Parlog program consists of a finite set of
.b "guarded clauses"
and
.b "mode declarations".
A guarded clause has the form:
.nf
.ta 1i 5.5iR
\f(CB        H <- G\*<1\*>, ..., G\*<m\*> : B\*<1\*>, ..., B\*<n\*>  (m,n >= 0) \fR
.r
.fi

where
.i H
is called the
.b "clause head",
.i "G\*<1\*>, ..., G\*<m\*>"
is its
.b guard
and
.i "B\*<1\*>, ..., B\*<n\*>"
its
.b body .
The \(lq:\(rq is a
.b "commit operator" .
If the guard is empty (i.e.
.i m
= 0) then the commit operator can be omitted.
.lp
The clause head
.i H
and each of the
.i "G\*<1\*>, ..., G\*<m\*>"
and
.i "B\*<1\*>, ..., B\*<n\*>"
are
.b goals .
A goal has the form:
.nf
.ta 1.5i 4.5iR
\f(CB        R(T\*<1\*>, ..., T\*<k\*>)      (k >= 0) \fR
.r
.fi

where
.i R
is the
.b "relation name"
and
.i "T\*<1\*>, ..., T\*<k\*>"
are its
.b "argument terms" .
The number of arguments,
.i k
is called the goal's
.b arity.
If the goal has no arguments (i.e.
.i k
= 0) then the brackets are
omitted.
A
.b relation
or
.b procedure
.c R/k
comprises all clauses with the relation name
.i R
and arity
.i k .

A
.b "mode declaration"
is associated with a Parlog procedure. For a relation
.c R/k
this has the form:
.nf
\f(CB        mode R(m\*<1\*>, .., m\*<k\*>).  \fR
.r
.fi

where each
.i m\*<i\*>
is either \(lq\fB?\fR\(rq or \(lq\fB^\fR\(rq.
A \(lq\fB?\fR\(rq denotes an
.b input
mode and a \(lq\fB^\fR\(rq denotes an
.b output
mode. If a mode declaration is omitted for a relation
.i R
then all of its argument terms are assumed to have an input mode.
A mode
.i m\*<i\*>
can be prefixed with argument names; these are comments of no semantic
significance.

A
.b term
can be a variable, constant or structured term. A
.b variable
is denoted by an identifier beginning with a capital letter or
the underscore character \(lq_\(rq. Examples are
.c X
and
.c Tree .
The underscore character may also be used on its own to denote a unique
.i anonymous
or
.i singleton
variable.
A
.b constant
is either an integer or a name denoted by any character string, which
must be enclosed in single quotes when it can be
confused with other types of term;
examples are
.c jim ,
.c "'Hello World'"
and
.c $primitive .
A
.b "structured term"
is denoted by:
.nf
\f(CB        F(T\*<1\*>, ..., T\*<n\*>)      (n >= 1)  \fR
.r
.fi

where
.i F
is the
.b "functor name"
and the
.b arguments
.i "T\*<1\*>, ..., T\*<k\*>"
are terms.  An example of a structured term is
.c point(X,Y,Z) ,
whose functor name is
.c point
of arity 3, with arguments
.c X ,
.c Y
and
.c Z .
Infix notation may be used for certain structured terms where the
functor is defined as an operator; thus the term
.c "=(X,Y)"
can be written as
.c "X\|=\|Y".

A special syntax is used for the type of structured term known as
.b lists .
The notation
.b [H|T]
is used to denote the list with the head
.i H
and tail
.i T .
A nested list term
.c [X|[Y|Z]]
may be abbreviated as
.c [X,Y|Z]
and if
.i Z
is the empty list or
.b nil
term (denoted by constant
.c [] )
then this can be abbreviated to
.c [X,Y] .

A Parlog program is invoked by executing a
.b query .
A query is a conjunction of goals:
.nf
\f(CB        ?- Q\*<1\*>, ..., Q\*<n\*>      (n >= 1)  \fR
.r
.fi

Goals in a clause or query may be separated by the
.b "parallel conjunction"
operator, \(lq\fB,\fR\(rq, or the
.b "sequential conjunction"
operator, \(lq\fB&\fR\(rq. Similarly, clauses in a relation
may be separated by Parlog's
.b parallel
and
.b "sequential clause search"
operators, denoted as \(lq\fB.\fR\(rq and \(lq\fB;\fR\(rq respectively.
In addition, the last clause of a relation is always terminated by
\(lq\fB.\fR\(rq.

Programs may include comments, which are defined as any text following a
\(lq\fB%\fR\(rq character up to the next newline character.


.sh 1 "SEMANTICS"

Parlog differs from Prolog in three important
respects : concurrent evaluation, don't-care
non-determinism, and its (optional) use of mode
declarations to specify communication constraints on
shared variables.

During the evaluation of a call r(t1',...,tk'),
all of the clauses for the predicate 'r' can be
searched in parallel for a candidate clause. The
clause :
.nf
\f(CB       r(t1, . . ., tk)  <-  < guard >  :  < body >  \fR
.r
.fi

is a candidate clause if the head r(t1,...,tk)
matches the call r(t1',..., tk') and the guard succeeds.
It is a non-candidate if the match or the guard fails. If
all clauses are non-candidates the call fails, otherwise
one of the candidate clauses is selected and the call is
reduced to the substitution instance of the body of that
clause.

There is no backtracking on the choice of
candidate clause since we
.i "don't care"
which candidate
clause is selected. In practice, the language
implementation dictates that the first one
(chronologically) to be found is chosen.
The absence of backtracking in Parlog is
compensated for by guard tests which can be used to
guarantee that the correct clause is chosen. This idea has
been more formally stated in Gregory's 
.b "sufficient guards law"
[Gregory 1987], which identifies the situations under
which a Parlog computation returns a solution to a query,
if a solution exists. It states that Parlog predicates
should be written so that guards in each clause guarantee
that if a clause is selected to reduce a goal then either
a solution to the call can be
computed using this clause, or
no solution can be found using any
other clause.
 
The search for a candidate clause can be
controlled by using either the parallel clause search
operator '.' or the sequential clause search operator ';'
between clauses. For instance, if a predicate is defined
by the clauses :
.(l
.ta 4i
.ft CB
Clause1  .
Clause2  ;
Clause3.
.)l
 
Then Clause3 will not be tried for candidacy until both
Clause1 and Clause2 have been found to be non-candidate
clauses.
 
Non-variable terms that appear in input argument
positions in the head of a clause can only be used for
input matching. If an argument of the call is not
sufficiently instantiated for an input match to succeed,
the attempt to use the clause suspends until some other
process further instantiates the input argument of the
call.
If all clauses for a call are suspended, the
call suspends but a candidate clause can be selected even
if there are other suspended clauses.
 
Parlog utilises unification for output bindings
which means that bindings to output variables will be
unified with any values in those argument positions in the
goal.

.bp
.sh 1 "COMMAND LINE OPTIONS"

.(l
.ba +5
.ip "\fC-n\fR \fIx\fR"
Specifies the number of processors to execute. The default value is -n1.
.ip "\fC-m\fR \fIx\fR"
Specifies the number of memory segments (i.e. 64Kbyte blocks) to
allocate on startup. The default value is 7 + 5 \(mu \fIn\fR, where
\fIn\fR is the number of processors.
.ba -5
.)l

On machines that allow shared memory to be expanded during execution
there should be no reason to use the \fC-m\fP option as the system can
allocate extra memory if needed automatically. Currently the Alliant,
Sequent Balance and Symmetry and the uniprocessor versions of the
system support this memory expansion feature.


.sh 1 "SYSTEM OPERATOR DECLARATIONS"

The following list specifies the operators defined in the system operator set.

.(l
op(system, 1400, xfy, ';')  &
op(system, 1350, xfy, '..') &
op(system, 1300, xfy, '\e\e') &
op(system, 1200, xfx, (<-)) &
op(system, 1200,  fx, (<-)) &
op(system, 1150,  fx, [(mode),(public),(prolog)]) &
op(system, 1150, xfx, :)    &
op(system, 1100, xfy, &)    &
op(system, 1050, xfy, and)  &
op(system, 1000, xfy, ',')  &
op(system,  700, xfx, [is,=,=..,\e=,==,\e==,<=,=@=,\e=@=,@<,@>,
                       @=<,@>=,=:=,=\e=,<,>,=<,>=])   &
op(system,  600, xf , [?,^])   &
op(system,  500, yfx, [+,-,/\e,\e/,\e]) &
op(system,  500,  fx, [+,-,\e])  &
op(system,  400, yfx, [*,/,//,<<,>>]) &
op(system,  300, xfx, [div,mod])      &
op(system,  200, xfx, #).
.)l

.sh 1 "SYSTEM PREDICATES"

.sh 2 "UNIFICATION RELATED"

.(x
Term1? = Term2?
.)x
\fBTerm1 = Term2\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Output unification.
Attempts to unify Term1 with Term2. Succeeds if Term1 and Term2 unify;
else fails.

.lp
.(x
Term1? == Term2?
.)x
\fBTerm1 == Term2\fR
.ip
\fIsynchronisation:\fR Suspends if Term1 and Term2 can be unified only by
binding variables in both of them.
.ip
\fIbehaviour:\fR Test unification. It unifies Term1 and Term2 without binding
variables in either term. Succeeds if Term1 and Term2 are identical terms;
fails as soon as Term1 and Term2 fail to match.

.lp
.(x
Term1? \e== Term2?
.)x
\fBTerm1 \e== Term2\fR
.ip
\fIsynchronisation:\fR As for 
.c ==/2 
above.
.ip
\fIbehaviour:\fR Tests Term1 and Term2 without binding variables in either.
Fails if Term1 and Term2 are identical terms; succeeds as soon as Term1 and
Term2 fail to match.

.lp
.(x
Term1? <= Term2?
.)x
\fBTerm1 <= Term2\fR
.ip
\fIsynchronisation:\fR Suspends if Term1 and Term2 could only be unified by
binding variables in Term2
.ip
\fIbehaviour:\fR Input matching.  Unifies Term1 and Term2 without binding
variables in Term2.  Succeeds if Term2 is a substitution instance of Term1.
Fails as soon as Term1 and Term2 fail to match.

.lp
.(x
var(X?)
.)x
\fBvar(X)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Succeeds if X is an unbound variable.  Fails if it is
instantiated.

.lp
.(x
nonvar(X?).
.)x
\fBnonvar(X)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Fails if X is an unbound variable.  Succeeds if it is
instantiated.

.lp
.(x
ground(X?).
.)x
\fBground(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is ground.
.ip
\fIbehaviour:\fR Succeeds when X is ground.

.lp
.(x
data(X?).
.)x
\fBdata(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated.
.ip
\fIbehaviour:\fR Succeeds when X is instantiated.

.sh 2 "TYPE TESTS"

.lp
.(x
atom(X?)
.)x
\fBatom(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated. 
.ip
\fIbehaviour:\fR Succeeds if X is an atom; otherwise fails.

.lp
.(x
atomic(X?)
.)x
\fBatomic(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated. 
.ip
\fIbehaviour:\fR Succeeds if X is an atom or a number; otherwise fails.

.lp
.(x
integer(X?)
.)x
\fBinteger(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated. 
.ip
\fIbehaviour:\fR Succeeds if X is an integer; otherwise fails.

.lp
.(x
float(X?)
.)x
\fBfloat(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated. 
.ip
\fIbehaviour:\fR Succeeds if X is a floating point number; otherwise fails.

.lp
.(x
number(X?).
.)x
\fBnumber(X)\fR
.ip
\fIsynchronisation:\fR Suspends until X is instantiated. 
.ip
\fIbehaviour:\fR Succeeds if X is an integer or a floating point number; otherwise
fails.

.sh 2 "ARITHMETIC EXPRESSIONS"

.lp
.(x
Value^ is Expression?
.)x
\fBValue is Expression\fR
.ip
\fIsynchronisation:\fR Suspends until Expression is ground.
.ip
\fIbehaviour:\fR Expression is evaluated as an
arithmetic expression, and the resulting number is unified with Value. If any of the
variables are bound to non-numeric values, then the goal fails.
An arithmetic expression is a term built from numbers, variables, and functors that
represent arithmetic functions. The functors available in Parlog
are given below.
In the following, X and Y are considered to be 
arithmetic expressions.

.(l
.ba +5
.ip +X 20
Results in the positive value of X. The type of the result is the same as
the type of the operand.

.ip -X 20
Results in the negative value of X. The type of the result is the same as 
the type of the operand.

.ip "\eX" 20
Results in the complements of the bits in X.

.ip integer(X) 20
Results in X if it is an integer. Otherwise, if it is a float, the result
is the nearest integer that is between it and 0.

.ip float(X) 20
Results in X if it is a float. Otherwise, if it is an integer, the result
is the floating point equivalent.

.ip X+Y 20
Results in the sum of X and Y. If both operands are integers, the
result is an integer; otherwise the result is a float.

.ip X-Y 20
Results in the difference of X and Y. If both operands are integers, the 
result is an integer; otherwise the result is a float. 
 
.ip X*Y 20
Results in the product of X and Y. If both operands are integers, the  
result is an integer; otherwise the result is a float. 
  
.ip X/Y 20
Results in the quotient of X and Y. The resultant value is always
a float, regardless of the types of the operands.   

.ip X//Y 20
Results in the integer quotient of X and Y. The resultant value is always
a (truncated) integer.

.ip "X mod Y" 20
Results in the remainder after the integer division of X by Y.
The result always has the same sign as X.

.ip "X/\eY" 20
Results in the bitwise conjunction of X and Y.

.ip "X\e/Y" 20
Results in the bitwise disjunction of X and Y.

.ip "X<<Y" 20
X is left-shifted Y places.

.ip "X>>Y" 20
X is right-shifted Y places with sign extension.

.ba -5
.)l

.sh 1 "TERM COMPARISON"

.(x
X?=@=Y?
.)x
\fBX=@=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically equal to Y; else fails.

.lp
.(x
X?\e=@=Y?
.)x
\fBX\e=@=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically not equal to Y; else fails.

.lp
.(x
X?@<Y?
.)x
\fBX@<Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically less than Y; else fails.

.lp
.(x
X?@>Y?
.)x
\fBX@>Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically more than Y; else fails.

.lp
.(x
X?@=<Y?
.)x
\fBX@=<Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically less than or equal to Y; else fails.

.lp
.(x
X?@>=Y?
.)x
\fBX@>=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR Succeeds if X is lexically more than or equal to Y; else fails.

.lp
.(x
X?=:=Y?
.)x
\fBX=:=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers,
and the goal succeeds if they are equal.

.lp
.(x
X?=\e=Y?
.)x
\fBX=\e=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers,
and the goal succeeds if they are not equal.

.lp
.(x
X?<Y?
.)x
\fBX<Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers,
and the goal succeeds if X is strictly less than
Y.

.lp
.(x
X?>Y?
.)x
\fBX>Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers,
and the goal succeeds if X is strictly greater than
Y.

.lp
.(x
X?=<Y?
.)x
\fBX=<Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers, and
the goal succeeds if X is less than or
equal to Y.

.lp
.(x
X?>=Y?
.)x
\fBX>=Y\fR
.ip
\fIsynchronisation:\fR Suspends until X and Y are instantiated.
.ip
\fIbehaviour:\fR X and Y must be numbers, and
the goal succeeds if X is greater than or
equal to Y.

.sh 2 "TERM MANIPULATION"

.lp
.(x
name(Atomic?,String?)
.)x
\fBname(Atomic,String)\fR
.ip
\fIsynchronisation:\fR Suspends until either Atomic or String is instantiated.
.ip
\fIbehaviour:\fR If Atomic is instantiated (it must be an atom or a number),
then string is unified with the list of character codes in its name.  If String is
ground (it must be a list of character codes), Atomic is unified with the number
if possible, otherwise the atom containing those characters.  Note that in Parallel
Parlog "add", for example, is shorthand for [97,100,100].

.lp
.(x
Term? =.. List?
.)x
\fBTerm =.. List\fR
.ip
\fIsynchronisation:\fR Suspends until either Term or List is instantiated.
.ip
\fIbehaviour:\fR List is a list whose head is the atom corresponding to
the principal functor of Term and whose tail is a list of the arguments of 
Term. If Term is uninstantiated, then List must be instantiated either
to a list of determinate length whose head is an atom, or to a list
of length 1 whose head is a number. 

.lp
.(x
functor(Term?,Functor?,Arity?)
.)x
\fBfunctor(Term,Functor,Arity)\fR
.ip
\fIsynchronisation:\fR Suspends until Term is instantiated, or Functor and Arity
are instantiated.
.ip
\fIbehaviour:\fR If Term is instantiated, Functor and Arity will be bound to the
functor and arity of Term. If Functor and Arity are instantiated, Term will be
bound to the most general Term having this name and arity.

.lp
.(x
arg(N?,Term?,Arg^)
.)x
\fBarg(N,Term,Arg)\fR
.ip
\fIsynchronisation:\fR Suspends until N and Term are instantiated.
.ip
\fIbehaviour:\fR Initially, N must be instantiated to a positive integer
and Term to a compound term. The result of the call is to unify Arg with
the Nth argument of Term. (The arguments are numbered from 1 upwards).
If the initial conditions are not satisfied or N is out of range, the
call fails.

.lp
.(x
copy_term(Term1?,Term2^)
.)x
\fBcopy_term(Term1,Term2)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR A copy of Term1 with new variables is unified with Term2.

.sh 2 "METALEVEL"

.lp
.(x
call(Call?)
.)x
\fBcall(Call)\fR
.ip
\fIsynchronisation:\fR Suspends until Call is instantiated.
.ip
\fIbehaviour:\fR If Call is instantiated to an atom or compound term, then the
goal call(Call) is executed exactly as if that term appeared textually in its
place. If Call fails, then call(Call) fails. 
.c call/3
is also available but contains a fault [see section 11. of the Reference Manual].

.lp
.(x
Module?#Call?
.)x
\fBModule#Call\fR
.ip
\fIsynchronisation:\fR Suspends until Module and Call are bound.
.ip
\fIbehaviour:\fR Evaluates Call as it is defined in Module.  Succeeds if Call
succeeds.  Fails if Call fails.

.lp
.(x
fail
.)x
\fBfail\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Fails.

.lp
.(x
not(Call?)
.)x
\fBnot(Call)\fR
.ip
\fIsynchronisation:\fR Suspends until Call is bound.
.ip
\fIbehaviour:\fR This fails if the goal Call has a solution, and succeeds
otherwise. This is not real negation, which is not
possible in Parlog, but negation-by-failure meaning that P is not
provable.

.lp
.(x
halt
.)x
\fBhalt\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Terminates the Parallel Parlog session.

.lp
.(x
true
.)x
\fBtrue\fR
.ip
\fIsynchronisation:\fR No suspension
.ip
\fIbehaviour:\fR Always succeeds.

.sh 2 "INPUT AND OUTPUT"

.lp
Input and output is dealt with by utilising \fBstreams\fR.  A stream can refer
to a file or to a terminal, and must be either an input or an output stream.
At any time, there is one \fBcurrent input\fR stream and one
\fBcurrent output\fR stream.  Input and output predicates fall into two
categories:

1) Those that use the current input and output stream.

2) Those which take an explicit stream argument.

The standard streams are identified by \fBuser_input\fR, \fBuser_output\fR, or
\fBuser_error\fR. Other streams are identified by the term returned by
.c open/3
in its
.i Stream
argument (see below).

The current input and output streams are set to \fBuser_input\fR and
\fBuser_output\fR upon entry to the Parallel Parlog system.

.sh 3 "STREAMS"

.(x
open(File?,Mode?,Stream^)
.)x
\fBopen(File,Mode,Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until File and Mode are instantiated.
.ip
\fIbehaviour:\fR If Mode is the atom \fBread\fR, and File is an existing file
for which the user has read permission then it succeeds, unifying Stream with a term
representing an input stream for File.  If mode is the atom \fBwrite\fR and the
user has permission to write to the file then it succeeds, unifying Stream with a
term representing an output stream for File.  If mode is the atom \fBappend\fR
and File is an existing file for which the user has write permission then it
succeeds, unifying Stream with a term representing an output stream for
appending to File. Otherwise the predicate fails.

.lp
.(x
close(Stream?)
.)x
\fBclose(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR If Stream is an open stream then it succeeds and closes the stream;
otherwise it fails. Failure will also occur if Stream is
the stream for standard input or output.

.lp
.(x
set_input(Stream?)
.)x
\fBset_input(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR If Stream is an open stream then it succeeds, setting the
current input to the stream.  Otherwise it fails.

.lp
.(x
set_output(Stream?)
.)x
\fBset_output(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR If Stream is an open stream then it succeeds, setting the
current output to the stream.  Otherwise it fails.

.lp
.(x
current_input(Stream?)
.)x
\fBcurrent_input(Stream)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR This causes Stream to be unified with the current input stream.

.lp
.(x
current_output(Stream?)
.)x
\fBcurrent_output(Stream)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR This causes Stream to be unified with the current output stream.

.lp
.(x
clear_input(Stream?)
.)x
\fBclear_input(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Flushes the input buffer associated with the given stream.  For
terminal input this means throwing away the rest of the current line.  The
current line being the sequence of characters input but not yet read off the stream.

.lp
.(x
flush_output(Stream?)
.)x
\fBflush_output(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Flushes the output buffer associated with the given stream.

.sh 3 "CHARACTER I/O"

.lp
.(x
get0(Stream?,N^)
.)x
\fBget0(Stream,N)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Unifies N with the ASCII code of the next character from
the input stream Stream.
If end of file is encountered, N is unified with the atom
\fBend_of_file\fR. If the end of stream was reached on a previous input
command, then this call will give an error message and abort the
computation.


.lp
.(x
get(Stream?,N^)
.)x
\fBget(Stream,N)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Unifies N with the ASCII code of the next non-space
character from the input stream Stream. Space characters are all
those with ASCII codes less than or equal to 32; this includes
space, tab, linefeed and all control characters.
If end of file is encountered, N is unified with the atom
\fBend_of_file\fR. If the end of stream was reached on a previous input
command, then this call will give an error message and abort the
computation. 
.c get/2
should not be used with \fBuser_input\fR.

.lp
.(x
skip(Stream?,N?)
.)x
\fBskip(Stream,N)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and N are instantiated.
.ip
\fIbehaviour:\fR Advances the input stream Stream to immediately past the
character represented by the ASCII code N  
(N must be an integer). If end of file is encountered N
is unified with the atom \fBend_of_file\fR.
If N is not an integer, or is an integer outside the range 0..127,
then the call fails after issuing an error.

.lp
.(x
put(Stream?,N?)
.)x
\fBput(Stream,N)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and N are instantiated.
.ip
\fIbehaviour:\fR Writes the character represented by the ASCII code N to the
stream represented by Stream.  
If N is not an integer, or is an integer outside the range 0..127, 
then the call fails after reporting an error. 


.lp
.(x
nl(Stream?)
.)x
\fBnl(Stream)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Starts a new line on the output stream Stream.
It is equivalent to \fBput(Stream,10)\fR.

.lp
.(x
tab(Stream?,N?)
.)x
\fBtab(Stream,N)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and N are instantiated.
.ip
\fIbehaviour:\fR Writes N spaces to the output stream Stream. N must
be an integer; otherwise an error is issued and the call fails.

.lp
The following predicates are equivalent to their counterparts above, except that
they act upon the current input stream :

\fB        get0(N)      get(N)       skip(N)\fR

In an analogous manner, the predicates below act upon the current output
stream :

\fB        put(N)       nl               tab(N)\fR

.(x
get0(N^)
.)x
.(x
get(N^)
.)x
.(x
skip(N?)
.)x
.(x
put(N?)
.)x
.(x
nl
.)x
.(x
tab(N?)
.)x

.sh 3 "TERM I/O"

.(x
read(Stream?,Term^)
.)x
\fBread(Stream,Term)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream is instantiated.
.ip
\fIbehaviour:\fR Reads the next Parlog term from the input stream
Stream and unifies it with
Term.  The term must be terminated by a full stop followed by a space character
or a newline.  These will be consumed by the call but not returned in Term. 
When a syntax error is encountered, an error message is printed which indicates
where the error occurs in the input, and the call then fails.
If the end of the file is encountered then the atom \fBend_of_file\fR is unified
with Term. If the end of stream was reached on a previous input command, then
this call will produce an error message and abort the computation.

.lp
.(x
read(Stream?,OpSet?,Term^,Vars^)
.)x
\fBread(Stream,OpSet,Term,Variables)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and Opset are bound.
.ip
\fIbehaviour:\fR As for 
.c read/2 
except that the operator set given by OpSet is used to
parse the term, and Variables is unified with a list of (Atom,Variable) pairs
giving the atom representation of each variable in the term.

.lp
.(x
display(Stream?,Term?)
.)x
\fBdisplay(Stream,Term)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and Term are instantiated.
.ip
\fIbehaviour:\fR Writes the term Term to the output stream Stream
in prefix notation, with no quotes for
atoms that need quoting in order to be read back in.

.lp
.(x
write(Stream?,Term?)
.)x
\fBwrite(Stream,Term)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and Term are instantiated.
.ip
\fIbehaviour:\fR Writes the term Term to the output stream Stream 
according to the operator precedences of
the \fBsystem\fR operator set, with no quotes for atoms that need quoting
in order to be read back in [see section 4. of the Reference Manual].

.lp
.(x
writeq(Stream?,Term?)
.)x
\fBwriteq(Stream,Term)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and Term are instantiated.
.ip
\fIbehaviour:\fR Writes in the same way as 
.c write/2 ,
but with quotes around atoms that need quoting
in order to be read back in.

.lp
.(x
write_canonical(Stream?,Term?)
.)x
\fBwrite_canonical(Stream,Term)\fR
.ip
\fIsynchronisation:\fR Suspends until Stream and Term are instantiated.
.ip
\fIbehaviour:\fR Writes the term Term to the output stream Stream, 
with quotes around atoms that need
quoting in order to be read back in.

.lp
The predicates below are equivalent to their counterparts above, except that
they act upon the current output stream :

\fB      display(Term)           write(Term)\fR
\fB      writeq(Term)            write_canonical(Term)\fR

As expected, 
.c read/2 
has a short form for reading from the current input stream :

\fB      read(Term)  \fR

.(x
read(Term^)
.)x
.(x
display(Term?)
.)x
.(x
write(Term?)
.)x
.(x
writeq(Term?)
.)x
.(x
write_canonical(Term?)
.)x

.sh 1 "COMPILATION AND LOADING"

.(x
compile(File?)
.)x
\fBcompile(File)\fR
.ip
\fIsynchronisation:\fR Suspends until File is instantiated.
.ip
\fIbehaviour:\fR Compiles a Parlog source code file.  Initially, the
name File is sought as the input file; if that does not exist then File is tried
with '.par' appended to it. If still no match is found, an error is issued and
the call succeeds. If the compiler is not already loaded, then a call to
.c compile/1 
will automatically load it.

.lp
.(x
load(File?)
.)x
\fBload(File)\fR
.ip
\fIsynchronisation:\fR Suspends until File is instantiated.
.ip
\fIbehaviour:\fR Loads a Parlog object code file.  Initially, the name
File is sought; if that does not exist then File with '.o' appended is tried.
If still no match is found, an error is issued and the call fails.

.lp
.(x
public Preds?
.)x
\fBpublic Preds\fR
.ip
\fIsynchronisation:\fR Suspends until Preds is ground.
.ip
\fIbehaviour:\fR Preds is expected to be a sequence of Name/Arity pairs that
represent predicate names, otherwise the call fails.  If a call to
.c public/1
succeeds, these predicates become visible to all other modules.

.lp
.(x
export(Predicates?)
.)x
\fBexport(Predicates)\fR
.ip
\fIsynchronisation:\fR Suspends until Predicates is ground.
.ip
\fIbehaviour:\fR The predicates list is expected to consist of Name/Arity pairs
that represent predicate names, otherwise the call fails. This enables the
predicates listed (which should be private to this module) to be imported
into other modules with
.c import/2 .

.lp
.(x
import(Module?,Predicates?)
.)x
\fBimport(Module,Predicates)\fR
.ip
\fIsynchronisation:\fR Suspends until Module and Predicates are ground.
.ip
\fIbehaviour:\fR The Predicates list is expected to consist of Name/Arity pairs that represent predicate names, otherwise the call fails. This imports the
definitions of Predicates from Module into the current module and can be
used by code subsequently loaded into the current module.

.lp
.(x
module(Module?)
.)x
\fBmodule(Module)\fR
.ip
\fIsynchronisation:\fR Suspends until Module is instantiated.
.ip
\fIbehaviour:\fR Makes Module the current module.

.lp
.(x
current_module(Module^)
.)x
\fBcurrent_module(Module)\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Unifies Module with the current module.

.sh 2 "MISCELLANEOUS"

.lp
.(x
op(OpSet?,Prec?,Type?,Op?)
.)x
\fBop(OpSet,Precedence,Type,Op)\fR
.ip
\fIsynchronisation:\fR Suspends until all four arguments are instantiated.
.ip
\fIbehaviour:\fR Inserts atoms into the operator set OpSet with the specified
precedence and type.  Precedence should be an integer between 1 and 1200, Type
should be one of fx, fy, yf, xfx, xfy or yfx, and Op should be an atom or a list
of atoms.

.lp
.(x
current_op(OpSet?,Prec^,Type?,Op?)
.)x
\fBcurrent_op(OpSet,Precedence,Type,Op)\fR
.ip
\fIsynchronisation:\fR Suspends until OpSet,Type and Op are instantiated.
.ip
\fIbehaviour:\fR Returns the precedence of the Op operator declared
in the OpSet operator set.  Type should be one of fx, fy, yf, xfx, xfy or yfx,
Op should be an atom, and OpSet should be an operator set.
If no match is found in OpSet, but an operator is found
in the 
.i system 
operator set, then the precedence of the system operator is returned.

.lp
The following two system predicates are equivalent to their counterparts above,
except that they act upon the operator set
.i system  :

.(x
op(Prec?,Type?,Op?)
.)x
\fB          op(Prec,Type,Op)          current_op(Prec,Type,Op)\fR

.(x
current_op(Prec^,Type?,Op?)
.)x

.(x
util(Package)
.)x
\fButil(Package)\fR
.ip
\fIsynchronisation:\fR Suspends until Package is bound.
.ip
\fIbehaviour:\fR Loads predicates from the specified package in the
utilities directory; an example is the SPM compatibility package
[see section 9. of the Reference Manual].

.lp
.(x
statistics(Key?,Val^)
.)x
\fBstatistics(Key,Val)\fR
.ip
\fIsynchronisation:\fR Suspends until Key is bound.
.ip
\fIbehaviour:\fR When Key is one of the atoms 
\fBsuspensions\fR,
\fBgcs\fR, \fBheap\fR, \fBstack\fR, \fBprocess\fR, \fBprogram\fR,
\fBmemory\fR, \fBruntime\fR or \fBcalls\fR
the appropriate values will be unified with Val and the predicate
will succeed; otherwise it will fail.  For memory values
(when Key has the value \fBheap\fR, \fBstack\fR, \fBprocess\fR, \fBprogram\fR
or \fBmemory\fR), Val is bound to a term of the
form [\fImemory_used, memory_free\fR]. These values are given in \(lqword\(rq
(4 byte) units except for \fBprocess\fP, which is given in
\(lqprocess\(rq (32 byte) units, and \fBmemory\fP which returns
the total size of the allocated and free memory segments in bytes.
When Key is \fBruntime\fP,
Val is bound to
a list of the form [\fItotal\fR, \fIlast\fR], where \fItotal\fR is the user
cpu time since the Parallel Parlog system was invoked, and \fIlast\fR is the
time since the last call to 
.c statistics/2 
or 
.c statistics/0 .

.lp
.(x
statistics
.)x
\fBstatistics\fR
.ip
\fIsynchronisation:\fR No suspension.
.ip
\fIbehaviour:\fR Prints out all the values generated by 
.c statistics/2 
onto the
current output stream.

.lp
.(x
unix(Command?)
.)x
\fBunix(Command)\fR
.ip
\fIsynchronisation:\fR Suspends until Command is instantiated.
.ip
\fIbehaviour:\fR Can be used in the following ways:

.lp
.ta 1i 3i
.nf
	unix(cd)	% change working directory to home directory
	unix(cd(Path))	% change working directory to Path
	unix(shell)	% invoke an interactive shell
	unix(system(Cmd))	% pass Cmd to sh(1)
	unix(access(File))	% test whether File is accessible (readable)

.fi

.lp
.(x
load_foreign(Preds?,Obj?,Libs?)
.)x
\fBload_foreign(Predicates,Obj,Libraries)\fR
.ip
\fIsynchronisation:\fR Suspends until all three arguments are instantiated.
.ip
\fIbehaviour:\fR Loads C prediates into the system.
See section 8. of the Reference Manual.

.lp
.(x
load_foreign(Preds?,Obj?)
.)x
\fBload_foreign(Predicates,Obj)\fR
.ip
\fIsynchronisation:\fR Suspends until both arguments are instantiated.
.ip
\fIbehaviour:\fR As for
.c load_foreign/3 
but with no libraries specified.
 
.lp
.(x
load_foreign_io(Preds?,Obj?,Libs?)
.)x
\fBload_foreign_io(Predicates,Obj,Libraries)\fR
.ip
\fIsynchronisation:\fR Suspends until all three arguments are instantiated.
.ip
\fIbehaviour:\fR Loads C prediates into the system that can only be executed
by the master processor.
See section 8. of the Reference Manual.

.lp
.(x
load_foreign_io(Preds?,Obj?)
.)x
\fBload_foreign_io(Predicates,Obj)\fR
.ip
\fIsynchronisation:\fR Suspends until both arguments are instantiated.
.ip
\fIbehaviour:\fR As for
.c load_foreign_io/3
but with no libraries specified.

.lp
.(x
spy(Pred?)
.)x
\fBspy(Pred)\fR
.ip
\fIsynchronisation:\fR Suspends until Pred is instantiated.
.ip
\fIbehaviour:\fR Pred must be a Name/Arity pair representing a predicate
that has already been loaded. If this is the case, 
.c spy/1
succeeds, setting spypoints on the named
predicate; otherwise it fails.

.lp
.(x
nospy(Pred?)
.)x
\fBnospy(Pred)\fR
.ip
\fIsynchronisation:\fR Suspends until Pred is instantiated.
.ip
\fIbehaviour:\fR Pred must be a Name/Arity pair representing a predicate
that has already been loaded.
If this is the case, 
.c nospy/1
succeeds, removing spypoints from the named
predicate; otherwise it fails.

.bp
.sh 1 "FOREIGN ROUTINES"

The Parallel Parlog system allows C predicates to be loaded dynamically
into the system either as predicates to be executed on any processor (using
.c load_foreign/3 ),
or predicates only executable by the master processor (using
.c load_foreign_io/3 ).

To aid integration of C routines into the Parlog environment, a
.b foreign
package is provided, for generating the
.i interface
routines that convert Parlog data types into C data types and invokes
the C routines.  The interface routines are generated from a file of
.c foreign/2
declarations, which have a similar format to those used by Quintus Prolog.

.sh 2 "Foreign declarations"

The syntax of the foreign declarations is as follows :

.(l
.c
foreign( Function, PredicateSpecification ).

PredicateSpecification = PredicateName(ArgSpec, ArgSpec,...)

.ta 2.5i
ArgSpec =  Arg?	\fI\(em input argument\fP
        |  Arg^	\fI\(em output argument\fP
        |  [Arg^]	\fI\(em argument for the function return value\fP

Arg = short | integer | float | string | atom
.r
.)l

In this context,
.i string
refers to a Parlog atom which is converted to a C string; whereas
.i atom
refers to the internal representation of a Parlog atom (a tagged Word)
which is passed directly to the C routine as a long unsigned integer.

The following conversions are carried out according to the ArgSpec type :

.nf
.ta 0.75i 2.25i 2.75i
	Parlog: \fCshort?\fP	-->	C: \fCinteger\fP
	Parlog: \fCinteger?\fP	-->	C: \fCinteger\fP
	Parlog: \fCfloat?\fP	-->	C: \fCdouble\fP
	Parlog: \fCstring?\fP	-->	C: \fCchar *\fP
	Parlog: \fCatom?\fP	-->	C: \fCWord\fP

	Parlog: \fCshort^\fP	<--	C: \fCinteger *\fP
	Parlog: \fCinteger^\fP	<--	C: \fCinteger *\fP
	Parlog: \fCfloat^\fP	<--	C: \fCdouble *\fP
	Parlog: \fCstring^\fP	<--	C: \fCchar **\fP
	Parlog: \fCatom^\fP	<--	C: \fCWord *\fP

	Parlog: \fC[short^]\fP	<--	C: \fCreturn (integer)\fP
	Parlog: \fC[integer^]\fP	<--	C: \fCreturn (integer)\fP
	Parlog: \fC[float^]\fP	<--	C: \fCreturn (double)\fP
	Parlog: \fC[string^]\fP	<--	C: \fCreturn (char *)\fP
	Parlog: \fC[atom^]\fP	<--	C: \fCreturn (Word)\fP
.fi

Some examples of foreign declaration are :

.(l
.ps -1
.vs -1
.ft CB
    foreign( sin, sin(float?, [float^]) ).
    foreign( rename, rename(string?, string?) ).
    foreign( test, tester(integer?, [integer^], atom?, atom^) ).
.r
.)l

.sh 2 "Adding foreign routines"

The procedure for intergrating a C routine into the Parallel Parlog environment
is as follows:

.ip 1.
Consider a C routine 
.c strcat()
which takes two strings as its arguments
and returns the concatenated string. It is defined in
.b strcat.c ,
and is compiled to an object file in the usual way :
.(l
.ps -1
.ft CB
cc -O -c strcat.c
.)l

.ip 2.
A foreign declaration for this C routine is then placed in a Parlog file,
called
.b "iface.par" :
.(l
.ps -1
.ft CB
foreign(strcat, strcat(string?, string?, [string^])).
.)l
This states that the Parlog predicate
.c strcat/3
will call the C routine
.c strcat()
and return its result as a Parlog atom in its 3rd argument.

.ip 3.
Now the interface file for this declaration can be generated by
loading the foreign package and calling
.c make_interface/1
in the following way :
.(l
.ps -1
.vs -1
.c
.sp 0.5
| ?- \f(CButil(foreign).\fP
[loading foreign]

yes
| ?- \f(CBmake_interface(iface).\fP

yes
.)l

This produces a C file called
.b iface.c
which must be then be compiled to an object file :
.(l 
.ps -1
.ft CB
cc -O -c iface.c
.)l

.ip 4.
The interface and C routine object files can now be loaded in to
the Parlog system with the 
.c load_foreign/3
command :
.(l
.ps -1
.c
| ?- \f(CBload_foreign([strcat/3], 'iface.o', 'strcat.o').\fP
.)l

.c strcat/3
is the name of the Parlog interface predicate,
.b iface.o
is the object file for the C interface and
.b strcat.o
is the object file for the original C routine.
Now the
.c strcat/3
predicate can be invoked :
.(l
.ps -1
.vs -1
.c
.sp 0.5
| ?- \f(CBstrcat(foo, bar, X).\fP
X = foobar

yes
.)l

.lp
In practise, an interface file can specify the interfaces
to C routines in several files. In that situation, the 3rd argument of
.c load_foreign/3
or
.c load_foreign_io/3
will contain several files and/or libraries, e.g.

.(l
.ps -1
.c
| ?- \f(CBload_foreign_io([a/1, b/1, ..], 'iface.o', 'win1.o win2.o -lXlib').\fP
.)l

.bp
.sh 1 "AN ALTERNATIVE TRACER"

The
.b trace
utility provides a more comprehensive tracing facility which allows
the user to execute a Parlog program with set break points and trace points.
At a trace point the
user is informed of some action which took place during execution. 
At a break point execution is halted and the user is able to investigate the
current state of the system. 

The basis behind this tracer is that each clause in a Parlog program may be
considered as a rewrite rule. Any point in the execution of a query
by the program may be represented by a list of goals. This list starts as
the initial query, and the rewrite rules are continually applied to it until
either it is empty, in which case the query succeeds, or no rewrite rule may be
applied, in which case the query fails. The tracer also halts at deadlock,
when no goal is sufficiently instantiated to allow a commitment to a rewrite
to take place.

.sh 2 "Loading and using the tracer"

The tracer is loaded with the command: \f(CButil(trace)\fP.
The program to be traced should be in one or more files named \fB<name>.par\fP.
To enter the tracer type :
.(l
.ps -1
.ft CB
partrace([<name\s-2\d1\u\s+2>,<name\s-2\d2\u\s+2>, ... , <name\s-2\dn\u\s+2>]).
.)l
where [<name\s-2\d1\u\s+2>,<name\s-2\d2\u\s+2>, ... , <name\s-2\dn\u\s+2>] is
a list of the filenames which contain the program to be traced
(without the \fB.par\fP suffix).
The brackets may be omitted if there is only one file.
The program will then be parsed by the tracer after which the
tracer will query the user for the sort of tracing required.

The system will first prompt with:
.(l
.ps -1
.c
 Full trace?
.)l

The user should reply \fCy\fP for yes or \fCn\fP for no.
In full trace mode every time a goal commits to a clause the 
reduction made is printed out, a break is made in computation,
and the user is prompted for a trace command.

If the user chooses not to use full trace mode the system will first ask
for a list of predicates to be traced on reduction. These should be entered
one to a line and followed by an empty line. If there is any ambiguity over
arity a predicate name may be followed by a slash and its arity. 
For example :
.(l
.ps -1
.ft CB
pred1
pred2/2
pred2/3
pred3

.)l
If the user just types <return> following the prompt no
predicate will be traced.
At any point in the computation when a goal for one of the predicates listed
for tracing commits to a clause the reduction will be printed in the form:
.(l
.b
<goal> => <body of new clause with variable bindings passed through>
.)l
If output modes are used, any unifications giving output are also printed
in the form:
.(l
.b
<variable> <= <term>
.)l

Following the prompt for tracing,
the user is prompted for a list of predicates to break on reduction.
The list is entered in the same way as above. When a goal for a predicate 
in this second list commits, the reduction will be printed as before,
but execution will also halt, and the user will be prompted for a trace
command.

Whether in full trace mode or not, the user will also be asked for a list
of predicates to be traced on completion. This is a form of tracing based on
the procedural view of Parlog. It prints out the final form of a goal
when it has completed execution. A goal has completed execution either if
it reduces to the empty list or if it has reduced to a list of subgoals all
of which have completed execution. A completion is indicated by the trace:
.(l
.ps -1
.c
* Completed * \fB<goal>\fP
.)l

.sh 2 "Trace Commands"

At a break point, the user is asked for a command with the prompt:
.(l
.ps -1
.c
Trace command :
.)l

A <return> typed at this point will display the state of computation
(see below), otherwise one of the following commands are can be typed:
.(l
\fCc\fP \(em continue execution until next trace point
.sp 0.5v
\fCf\fP \(em give full display, including all goals following a sequential "&"
.sp 0.5v
\fCg\fP \(em give standard display and allow user to investigate goals
.sp 0.5v
\fCh\fP \(em give list of available commands
.sp 0.5v
\fCq\fP \(em quit execution
.sp 0.5v
\fCt\fP \(em reset trace information
.sp 0.5v
\fCu\fP \(em undo trace points and continue execution until completion
.)l

.sh 2 "Displaying state of computation"

At any point in the computation goals in the list of current goals will be
of three types:
.ip
1. Those on which no attempt has been made to reduce, but which could be chosen
for reduction.
.ip
2. Those on which an attempt to reduce has been made, but which have not yet
committed, possibly due to a suspension while awaiting a variable 
instantiation.
.ip
3. Those which cannot be reduced as they follow a sequential "&", and the goal
before the "&" has not completed execution.
.lp
The standard display only prints goals of the first two sorts,
numbering each goal. 
The full display prints every goal and includes the bracketing to indicate 
the mix of sequential and parallel execution.

Following the \(lqinvestigate goals\(rq command, \fCg\fP, the list of goals is 
displayed and the user may choose to investigate it in more detail. 
The system gives the following prompt:
.(l
.ps -1
.c
Enter goal number (0 to restart tracing) :
.)l

If an integer greater than 0 is entered, the system will give further details
on the goal with that number. If no attempt has been made to reduce the goal,
it will just be printed. Otherwise the current state of reduction is given.
The current state of the guard computation, and any input variables on which
a possible commitment is suspended are given, followed by the subgoals to
which the goal would reduce if commitment were made.
In the case of a sequential clause search, clauses which would be tried if
the current commitment cannot be made are displayed.

If \fC0\fP is entered instead of a goal number, the system returns to
the computation. Alternatively, the list of the goals can be redisplayed by
entering \fCl\fP, and can be displayed in full form by entering \fCf\fP.

.bp
.sh 1 "SPM COMPATIBILITY"

SPM [Foster et al. 1986]
and Parallel Parlog are syntactically very similar, and the system
predicates in both languages offer much the same functionality.
However, many SPM
builtins are not present in Parallel Parlog, but this
obstacle can be overcome by loading the compatibility package with
the command
.b "util(spm)"
It contains predicate definitions for
emulating certain SPM predicates from six broad
categories : I/O, unification, arithmetic, type testing, term manipulation
and utilities. The predicates defined in this package are :
.(l
.ft C
.r "1) I/O."
        fcreate/2      fclose/1      fopen/3
        loutput/1      loutputnl/1   p/1
        pnl/1          print_term/1  print-term/2
        read_char/2    write_char/2  load_files/1

.r "2) Unification."                              
        equal/2        unify/2       assign/2

.r "3) Arithmetic."                           
        add/3          div/3         eq/2
        less/2         lesseq/2      mod/3
        mul/3          sub/3

.r "4) Type testing."        
        con/1          list/1        tuple/1
 
.r "5) Term manipulation."
        a_to_s/2                     s_to_a/2
        list_to_struct/3             struct_to_list/3
        l_to_i/3                     i_to_l/2
        sappend/2                    predefined_op/1
 
.r "6) Utilities."
        defined/1      sleep/1

.)l                            
 
The SPM file manipulation primitives 
.c access/1 ,
.c assem/1
and
.c edit/3 
are not supported.

In the area of I/O, the
.c get_term/2
and
.c put_term/3
primitives for reading terms to and from a disk
are not implemented in Parallel Parlog, and
.c intok/3
and
.c iowait/2
are also not available.
 
The
.c set/3
and
.c subset/3
all-solution primitives
are absent from Parallel Parlog, but all of the type
testing primitives are present
(albeit after
.b "util(spm)"
is loaded). However, care must be taken when transferring user-defined
predicates of this sort across from the SPM, since
SPM does not treat [] as an atom while Parallel Parlog does.
 
Parallel Prolog has neither a
.c freeze/2
nor a
.c melt/2
primitive, but does offer
.c copy_term/2
for which they are most often used.
 
Unavailable control primitives include 
.c controlc/0 ,
.c key/1
and
.c space/3 .
 
Many SPM utilities are absent from Parallel Parlog, including
.c date/3 ,
.c show_date/0 ,
.c time/3 ,
.c instream/1 ,
.c load_comp/0 ,
.c sedit/0 ,
.c sinput/2
and
.c username/1 .
In addition, the utilities for type checking,
manipulating binary trees and the
.b xref
program are not supported.
Finally, Parallel Parlog does not support any window predicates,
although an X based window interface is planned for the future.

Parallel Parlog differs from SPM in having more predicates for testing types, a
set of lexical comparison primitives, a module system, a spypoint mechanism,
a C and UNIX interface, and statistic predicates. In
addition, Parallel Parlog enables
extra operator sets to be defined, and for commands to be included
in source files.
 
Some SPM programs may need to be rewritten to
take into account the nonflat guard restriction of Parallel Parlog.
However, transformation techniques exist to do this and these may be
put into future releases.

.sh 1 "MACPARLOG COMPATIBILITY"

MacParlog [Conlon and Gregory 1989]
is very similar to Parallel Parlog except in the area of I/O, where
it contains numerous primitives for manipulating the Macintosh user interface.

Some versions of MacParlog                                                     
can use arithmetic functions from MacProlog [Clark et al. 1987],
for such things as transcendental and exponential calculations.
 
In the
area of metalevel predicates, MacParlog allows terms to be
converted between ground and hollow versions using
.c toground/3
and
.c tohollow/3 ,
and
.c varsin/2
permits the variables in a term to be accessed.
.c primitives/1
and
.c private_macparlog_names/1
have no equivalents in Parallel Parlog.
 
I/O in MacParlog is based on
.b channels ,
which are like Parallel Parlog
.b streams
except that channels can be connected to Macintosh
windows. MacParlog input can utilise the mouse, and
.c key/0
is available for waiting on input.
.c gread/3
reads terms and returns the variables contained within them, but is
less flexible than
.c read/4
in Parallel Parlog.
.c skip/1
and
.c skip/2
are slightly different in MacParlog since the 'N' argument can
be a character as well as an integer.
 
File handling in MacParlog utilises the Macintosh
hierarchical filing system rather than UNIX directories. However, the
resulting predicates are still quite similar to
those in Parallel Parlog. The main difference is the use of file
dialogues in
.c create/2 ,
.c new/3
and
.c old/2
to access a particular file on a volume.

MacParlog has a number of extra utility
predicates not present in Parallel Parlog; namely
.c beep/1 ,
.c recall/2 ,
.c remember/2 ,
.c ticks/1
and
.c time/3 .
Also, the language permits deep guards to be tried in
parallel, and so does
not impose the nonflat guard restriction found in Parallel Parlog.
MacParlog has a number of window handling primitives and dialogue
primitives, none of which are available in Parallel Parlog.

Parallel Parlog has primitives for the lexical comparison of
terms, for gathering statistics, and accessing C and UNIX,
which are not found in MacParlog. Also, MacParlog
does not support user-defined
operator sets,
there is no module system, and
compiling and loading is done via pull down menus.
In addition, MacParlog does not allow commands to be included in source files.

.sh 1 "CHANGES BETWEEN V1.0 AND V1.4"

Most of the differences between the first and second releases of
Parallel Parlog are internal and transparent to the user. 
This section outlines the areas where the user may notice the changes.

As already mentioned, the memory management is quite different; in
.i "V1.0"
the sizes of the four main data areas \(mi code, heap, argument stack and
process stack \(mi were determined at startup time, and controlled by
.c -C ,
.c -H ,
.c -S
and
.c -P
options. In
.i "V1.4"
each area is made up of a linked list of
.i segments
or 64Kbyte blocks, which are allocated from a pool of segments.
The initial number of segments can be controlled by the
.c -m
option, but on machines in which (shared) memory is expandable, the
system will automatically allocate more when needed.

C predicates can now be executed on any processor if loaded with
.c load_foreign/3 ,
allowing Parlog to control the parallel execution of C routines.
Predicates which must only run on the master processor
(e.g. I/O predicates or perhaps window predicates)
should be loaded using
.c load_foreign_io/3
instead.

There have been some changes to the compiler and the code it generates.
One noticeable change is that calls to
.c is/2
in the body can now be executed \(lqinline\(rq, with a full goal call
only being made if the inline call cannot proceed because input arguments
are unbound. This optimisation cannot be done if the call to
.c is/2
precedes a sequential conjunction operator ("&") and is not in parallel with
any other goal calls - the compiler provides a warning message about this.

The
.c public/1
predicate has been changed so that it makes the specified predicates visible
in all modules. Before, only public predicates in the
.i system
module were visible to all modules; public predicates in other modules
still had to be explicitly imported.

There were a few problems related to the use of floating point numbers in
.i "V1.0"
which have been fixed in
.i "V1.4" .

.sh 1 "BUGS/FEATURES"

Suspended guard calls for a candidate clause, which has been rejected in
favour of another clause, are not removed by the system. Such suspended guard
calls are also called 
.b dead 
processes.

Deadlock and interrupts do not kill or remove processes; they only restart the
top level command interpreter.

I/O predicates produce a segmentation error when called with an
inappropriate stream argument.

It is possible to overflow a heap by unifying a variable with a very
large term in the source code (e.g. a list of over 128 elements) when the top
heap segment is almost full. This can result in a segmentation error.
There is no problem with terms constructed dynamically during execution
and a workaround is to break up the source code term into smaller parts, e.g.
.(l
.c
g(X) <- X = [1,2,....,128|X1], g1(X1).
g1(X1) <- X1 = [129,130,....,256].
.r
.)l

Similarly, it is possible to overflow an argument stack by calling a large
number of goals in a parallel conjunction when the top stack segment is
almost full, if the sum of all the arguments in the parallel conjunction
exceeds 256. The workaround is to replace some of the goals in the conjunction
with a single call to a predicate that invokes the replaced goals.
In theory, the compiler could be made to detect and fix both of these
cases automatically.

Loading C predicates does not work on the Alliant and Multimax versions.

.bp
.sh 1 "SYSTEM PREDICATES AT A GLANCE"

.2c
.xp
.1c

.uh REFERENCES
.rm (f
.rm )f

Clark, K., McCabe, F.G., Johns, N., and Spenser, C. 1987., 
.i "LPA MacPROLOG Reference Manual" ,
Logic Programming Associates Ltd, London.

Conlon, T., and Gregory, S. 1989.,
.i "Hands on  MacParlog  1.0" ,
Parallel Logic Programming Limited, London, January.

Crammond, J.A. 1990.,
.i "The Abstract Machine and Implementation of Parallel Parlog" ,
Research Report, Dept. of Computing, Imperial College, London.

Foster, I., Gregory, S., Ringwood, G., and Satoh, K. 1986.,
.i "A  Sequential  Implementation  of PARLOG" ,
3rd International Conference on Logic Programming,
London, July.

Gregory, S. 1987.,
.i "Parallel Logic Programming in  PARLOG : The  Language  and  its  Implementation" ,
Addison-Wesley.

