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 [] A:- G1,...,G_n | B1,...,B_m. where A is the head of the clause, G1,...,G_n the guard goals and B1,...,B_m 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.