$= \section{DLP = LP + OO + ||} \label{des/per/dlp} \dlpindex{design perspectives} Having sketched the motivation for developing the language DLP, we wish to take a step back, in order to reflect on the origins of DLP in the design space of computer programming languages. See for analog studies  [Da89a],  [Am89b]. At the end of this chapter we will ask ourselves whether or not there really is a need for such a language. Before justifying our own decisions we may well look at the alternatives we have in combining the three components of our language: logic programming, object oriented programming and parallelism. The diagrams below suggest three possible ways of arriving at such a combination. .so tb Either we may start from a combination of logic programming and object oriented programming (LP + OO) and add concurrency to it; or we may take a combination of logic programming and concurrency (LP + ||) and extend this to include object oriented programming; or we may take a concurrent object oriented language (OO + ||) and lift this in order to support logic programming. \nop{ To our satisfaction DLP fits to some extend in any of these diagrams. } According to this scheme we may classify a number of related approaches as in the table below. .so tx To indicate where DLP must be located in the table above, we may remark that our language is very much like MultiLog. In contrast to MultiLog, however, DLP supports backtracking over the results of a method call, a property it shares with the languages falling under the heading LP + OO, combining logic programming and object oriented programming. In the sections that follow, we will investigate the merits of DLP with respect to our classification scheme. For each of the combinations LP + OO, LP + || and OO + || we will try to delineate the central issue tackled and the problems encountered in completing the triangle. After describing the related approaches that exemplify that particular combination we will discuss the solutions provided in DLP.

Logic programming and objects

The central question in combining logic programming and object oriented programming is how to implement objects with internal states. Such states must be hidden. An object, in other words, must guarantee a certain protection with respect to the access and modification of its state by providing a suitable method interface. .so LogicalObjects .so SPOOL .so CPU In comparison with the approach taken for Logical Objects, that remains within the logic programming framework, the language SPOOL is of a more hybrid nature. A common characteristic of the three approaches sketched above is that they allow backtracking over method calls, which seems partly to be a result of embedding object oriented constructs in Prolog. The extent to which this is a deliberate design decision or an accidental quality due to the implementation is not altogether clear. Both Logical Objects and SPOOL are sequential languages. For Communicating Prolog Units, concurrency is introduced by allowing units, that are like objects, to be active.

DLP

supports objects with states primarily by what we have called non-logical variables. Passive objects merely respond to method calls, that may be regarded as the evaluation of a goal by an object. With respect to the backtracking behavior of such method calls we have decided to strive for full compatibility with Prolog. In other words, it must make no difference whether a goal is evaluated by means of a method call or in the ordinary way, provided the clauses needed for evaluating the goal are available. Having objects with non-logical variables complicates matters a bit. We have decided that backtracking over the results of a method call does not undo any modification to the non-logical variables of the object to which the call was addressed. This decision needs some justification. As a first observation, we wish to state that from the outside the non-logical variables of an object are invisible. Calling a method results in binding logical variables to some value, possibly in a number of alternative ways, or in failure. How these results are computed is the responsability of the object. Secondly, we may remark that we conceive of non-logical variables as an abstract representation of entities such as a database. Undoing modifications to such entities at every attempt at backtracking over a method call may lead to serious problems, in particular since in a logic programming context failure is a natural outcome of a computation. We have already made clear that we wish to distinguish between partial failure in the sense of hardware errors or the violation of integrity constraints and failure in the logic programming sense. To our mind, automatically undoing modifications to non-logical variables may be better handled by additional features such as atomic sections or recovery mechanisms. C.f.  [Kl85]. In the third place, we wish to point out that, although automatic recovery on backtracking may seem a feasible solution for a sequential language, when introducing concurrency a number of problems arise due to the interaction with other objects. What is the scope for which we must guarantee protection? And, how do we handle the possible interference by other method calls? $=

Logic programming and concurrency

In section \ref{des/per/concept} we have introduced concurrent logic programming as one of the paradigms of distributed computing. We have also sketched how to use a concurrent logic programming language to implement objects. In this section we will treat two languages that provide object oriented extensions to a concurrent logic programming language. .so Vulcan .so Polka A restriction adhering to both languages is the absence of backtracking over the results of a method call. As a remedy one could re-introduce backtracking by integrating a concurrent language with Prolog. C.f.  [Sh89]. Merely providing an interface between the two languages is not satisfactory, simply because it does not provide an integration of the two computation models underlying these languages. Embedding Prolog in a concurrent logic programming language, although feasible for non-flat versions allowing arbitrary goals in the guard, leads to efficiency problems. Moreover, as  [Sh89] observes, the implementation techniques for concurrent logic programming languages lag far behind those developed for sequential Prolog. C.f.  [He86]. Another option might be to extend Prolog to a language combining logic programming and concurrency. An example of such an approach is the language Delta Prolog. .so DeltaProlog When we compare Delta Prolog with the object oriented languages based on the concurrent logic programming model, the most obvious difference is that communication in Delta Prolog must be stated explicitly, whereas communication in the concurrent logic programming languages is mediated by shared logical variables. Another difference is that processes created for executing a concurrent logic program are usually fine-grained, whereas Delta Prolog gives rise to coarse grain parallelism. We note that Delta Prolog allows to implement objects in a similar way as the concurrent logic programming languages. We do not consider Delta Prolog to be object oriented, however.

DLP

supports concurrency in basically two ways. The first, perhaps most natural way, is to create active objects that execute their own activity in parallel with the activity of other objects. The second way is to let an object evaluate a number of method calls simultaneously. To implement this, a notion of processes has been introduced, distinct from objects. Each active object has a so-called constructor process associated with it, that handles the evaluation of the body of the object, as expressed in the constructor clauses. Moreover, for each method call a process is created to handle the backtracking information needed to generate all answers to the call, and to communicate these to the invoking process. When multiple processes are active for some object, we speak of internal concurrency, or multi-threaded objects. Passive objects allow unlimited internal concurrency. On the other hand, for active objects we have allowed such internal concurrency only for backtracking over alternative answers, after having delivered the first answer. The reason for this policy is that we wish to guarantee mutual exclusion between method calls for the time needed to produce the first answer. That is to say, no two method calls will be active similtaneously with producing their first results. A method call may however become active when other processes are still busy backtracking over the answers of a call. The language DLP provides primitives for synchronous communication over channels, that are rather similar to those offered by Delta Prolog. However, the backtracking that may arise during communication over channels in DLP is much more limited than the distributed backtracking supported by Delta Prolog, in that it occurs locally within the confines of the process stating the input goal. The rendez-vous supported by DLP does not suffer from such a restriction. Moreover, we claim that our rendez-vous allows to impose synchronization constraints that cannot be expressed in Delta Prolog. Additional parallelism may be achieved in DLP by using the primitives for process creation and resumption requests directly. These constructs allow to join the results of two independently running processes sharing logical variables. We have not encountered any such mechanism in the literature! We remark that these primitives have been used to implement the synchronous rendez-vous arising from a method call. To enable active objects to engage in a rendez-vous we have provided an accept statement for interrupting the own activity and to state the willingness to answer particular method calls. This construct has been inspired by languages combining the object oriented programming paradigm with concurrency. A notable difference between DLP on the one hand and Delta Prolog and the two object oriented languages based on the concurrent logic programming model on the other hand is that states of objects in DLP are kept in non-logical (instance) variables whereas in the three other approaches states are maintained as the argument of a tail-recursive predicate. We note that the conditional accept statement introduced in section \ref{des/ext/accept} allows a similar implementation of objects.

Objects and concurrency

A radically different approach at combining logic programming, object oriented programming and concurrency is to take an existent parallel object oriented language as a starting point and to attempt to lift it to a language supporting logic based computation. Such an approach is exemplified by the language MultiLog. As another example we wish to mention the language Orient84/K. .so MultiLog .so Orient Both MultiLog and Orient84/K support single-threaded objects only. One of the possible advantages of lifting a parallel object oriented language may be that the mechanisms for process creation and communication are to a certain extent available. However, implementing a Prolog interpreter is not altogether a trivial matter.

DLP

is, apart from a number of notational differences, quite similar to MultiLog. A notable exception to this similarity, however, is that, unlike MultiLog, DLP does support global backtracking over the results of a rendez-vous. In a sense our language DLP may be regarded as the result of lifting the language POOL to a logic programming language. See section \ref{impl/comp:lang} and  [Am87]. The problem we had to solve was to find the proper constructs for process creation and communication between processes. As a consequence, our initial design goal has been the extension of Prolog to a parallel object oriented language. Unlike MultiLog and the object oriented languages based on concurrent logic programming, we did not wish to give up compatibility with Prolog. DLP therefore supports the {\it don't know} non-determinism of backtracking.