Concurrent logic programming
\label{des/rel:CP}
\parindex{concurrent logic programming}
\disindex{concurrent logic programming}
The model underlying concurrent logic programming
forms a rather radical departure from the two previous models
in that communication is essentially effected through
shared logical variables.
Parallelism is inherent in the computation model
of logic programming languages, because of their declarative nature.
\parindex{{\em or} parallelism}
\parindex{{\em and} parallelism}
Basically, two kinds of parallelism can be distinguished:
and-parallelism that is due to the parallel evaluation of the atoms
in a compound goal,
and or-parallelism that arises from trying multiple clauses simultaneously
for finding a solution to a goal atom.
Although there are a number of attempts at implementing
parallel Prolog this way,
the two major representatives of concurrent logic programming,
Concurrent Prolog and Parlog, have based their approach
on the additional assumption of committed choice non-determinism
and restricted unification.
Committed choice
\parindex{committed choice}
\parindex{guarded Horn clauses}
Unlimited or-parallelism, required to find all solutions to a goal,
may result in an uncontrollable amount of processes.
To restrict or-parallelism, guarded Horn clauses were introduced.
A guarded Horn clause is a clause of the form
[]
where A is the head of the clause, the guard
goals and the actual body of the clause.
When a goal atom is evaluated, all clauses of which
the head unifies with the atom are selected and the
guards of these clauses are evaluated in parallel.
The first clause of which the guard is evaluated successfully
is committed to.
The alternative solutions to the goal atom,
embodied in the competing clauses, are thrown away.
Since only one clause is chosen, backtracking over alternative solutions
is impossible,
once the commitment to that particular clause is made.
What is allowed as a guard
influences the expressiveness of the language in a significant degree,
and for that matter the difficulty of implementing it.
See [Sh89] for an extensive discussion of this topic.
Restricted unification
\parindex{restricted unification}
Unrestricted and-parallelism,
that is the parallel evaluation of the atoms in a compound goal,
may result in incompatible bindings of the logical variables
involved.
To handle this problem,
both Concurrent Prolog and Parlog require to indicate
which atom acts as the producer of a binding to a variable and
which atoms are merely consuming the binding.
Concurrent Prolog uses annotations to indicate the variables
that must be bound to a term to enable the evaluation of
the atom in which they occur to proceed.
Parlog, on the other hand, uses mode declarations,
indicating the input/output behavior of the arguments of
a predicate.
Objects
\disindex{objects in concurrent logic programming}
Concurrent logic programming languages offer
a very versatile mechanism for implementing distributed
systems. C.f. [Sh89].
In particular these languages allow to implement active objects with
state variables in a very elegant way.
This is achieved by defining clauses for objects
according to the scheme presented below.
\oprog{object}{
obj(State, [Message|Messages]) :-
\{\it handle\} Message,
\{\it update\} State \{\it to \} State',
obj(State',Messages).
}
An object is implemented as a tail-recursive process,
that receives messages and updates its state if necessary.
C.f. [ST83].
As an example, consider the clauses implementing
a counter in Concurrent Prolog.
\oprog{ctr}{
ctr(N,[inc()T]):- N1 = N + 1, ctr(N1,T).
ctr(N,[value(N)T]) :- ctr(N,T).
}
The first argument of ctr represents the state of the object,
that is passed as an argument to the recursive call,
appropriately modified if necessary.
The second argument represents the stream of incoming messages,
with the tail unspecified to await later binding.
Concurrent logic programming languages offer fine-grained parallelism.
As an additional feature for dynamically mapping
computations to processes [Sh84] proposes a turtle notation
for executing Concurrent Prolog programs on a grid of processors.
See also section \ref{des/ext/alloc}.
Extensions
The primary advantage of using a concurrent logic programming
language for implementing distributed systems is the declarative nature
of these languages,
allowing a logical interpretation of a program.
This property is preserved when implementing objects
in the way shown.
To overcome the syntactical complexity of this approach,
two languages combining logic programming
and object oriented programming have been
proposed, Vulcan and Polka, that preserve the declarative
semantics of their underlying logic
programming languages.\ftn{
These languages will be discussed in section [Vulcan].
}
The drawback of this approach, however, is that the restrictions
imposed by the requirement of efficiency -- committed choice and restricted unification --
do not allow for the occurrence of backtracking.