The language DLP

The language DLP may be regarded as an extension of Prolog with object declarations and statements for the dynamic creation of objects, communication between objects and the assignment of values to non-logical instance variables of objects.

Object declarations

Object declarations in DLP have the form \dlpindex{object} \oprog{object}{
  object name {
  var variables.
  clauses
  }
  
} Both object and var are keywords. The variables declared by var are non-logical variables that may be assigned values by a special statement. Objects act as prototypes in that new copies may be made by so-called new statements. Such copies are called instances. Each instance has its private copy of the non-logical variables of the declared object. In other words, non-logical variables act as instance variables. Dynamically, a distinction is made between active objects and passive objects. Active objects must explicitly be created by a new statement. Syntactically, the distinction between active and passive objects is reflected in the occurrence of so-called constructor clauses in the declaration for active objects. Constructor clauses are clauses of which the head has a predicate name identical to the name of the object in which they occur. Constructor clauses specify an object's own activity. The other clauses occurring in an object declaration may be regarded as method clauses, specifying how a request to the object is handled. Passive objects only have method clauses.

Statements

DLP extends Prolog with a number of statements for dealing with non-logical variables, the creation of objects and the communication between objects. These statements may occur as atoms in a goal.

Non-logical variables

For assigning a term t to a non-logical variable x the statement \dlpindex{\assignexpr} <>- x := t is provided. Before the assignment takes place, the term t is simplified. The non-logical variables occurring in t are replaced by their current values. In fact, such simplification takes place for each goal atom. DLP also supports arithmetical simplification. \nop{ For accessing the value of a non-logical variable x of some object O a term of the form [] O@x is used. This term is simplified to the value of that non-logical variable when the atom in which it occurs is evaluated. }

New expressions

For dynamically creating instances of objects the statement \dlpindex{\newpassiveexpr} <>- O = new(c) is provided, where c is the name of a declared object. When evaluated as an atom, a reference to the newly created object will become bound to the logical variable O. For creating active objects the statement \dlpindex{\newactiveexpr} <>- O = new(c(t1,...,t_n)) must be used. The activity of the newly created object consists of evaluating the constructor goal c(t1,...,t_n), where c is the object name and t1,...,t_n denote the actual parameters. The constructor goal will be evaluated by using the constructor clauses. Actually, the expressions new(c) and new(c(t1,...,t_n)) will be simplified to a reference to an object when they occur as a term in a goal. Both the statements introduced above may be regarded as special cases, in which the new expressions occur in a unification goal.

Method calls

A method call is the evaluation of a goal by an object. To call the method m of an object O with actual parameters t1,...,t_n the statement \dlpindex{\methodcallexpr} <>- O!m(t1,...,t_n) must be used. It is assumed that O is a logical variable referring to the object to which the request is addressed. When such an atom is encountered, the object O is asked to evaluate the goal m(t1,...,t_n). If the object to which the call is addressed is willing to accept the request then the result of evaluating m(t1,...,t_n) will be sent back to the caller. After sending the first result, subsequent results will be delivered whenever the caller tries to backtrack over the method call. If no alternative solutions can be produced the call fails. Active objects must explicitly interrupt their own activity and state their willingness to accept a method call by a statement of the form \dlpindex{\acceptexpr} <>- accept(m1,...,m_n) which indicates that any request for one of the methods m1,...,m_n will be accepted.

The computation model of DLP

The computation model of DLP combines the computation model underlying Prolog and the model underlying a parallel object oriented language. Parallel object oriented processing must support objects, processes and communication between objects.

Objects

contain non-logical data, persisting during the life time of the object, and clauses defining the functionality of the object.\nop{ The clauses are identical to ordinary Prolog clauses, except for the possible occurrence of special atoms for creating new objects or for communicating with other objects. } Objects may be active or passive. The activity of an object is defined by so-called constructor clauses that describe the own activity of an object. Apart from constructor clauses, active objects may also contain so-called method clauses that are used when the object receives a method call. A method call is simply the request to evaluate a goal.

Processes

are created when creating a new active object and for the evaluation of a method call. The process executing the own activity of an active object is called the constructor process. For each method call a process is created to enable backtracking over the results of a method call. Passive objects have no activity but answering to method calls. Active objects must explicitly interrupt their own activity to indicate the willingness to answer a method call.

Communication

with another object takes place by engaging in a (synchronous) rendez-vous. In order to achieve compatibility with the ordinary Prolog goal evaluation, DLP supports global backtracking over the results of a rendez-vous. With respect to backtracking, it is transparent whether a goal is evaluated remotely, by another object or locally, provided the necessary clauses are defined. This transparency holds for both passive and active objects. Below is pictured what happens when a process issues a method call to an active object. Here we assume that accept statements may occur only in the constructor process associated with an (active) object. .so sv As soon as both the process calling the method and the constructor process of the object to which the call is addressed have synchronized, the activity of the constructor is interrupted and a process is created to evaluate the goal m(t1,...,t_n). The constructor is interrupted for safety reasons, in order to guarantee that no other method call will be accepted. The calling process waits for an answer. As soon as the caller has received a result, both the calling process and the constructor process resume their activity. On backtracking, the calling process may ask for alternative solutions. Passive objects allow unlimited internal concurrency: in other words, an indefinite number of method calls may be active simultaneously. For active objects, mutual exclusion is provided to the extent that when a particular method call is accepted no other method call will be accepted until the first answer for that call is delivered. This protocol of mutual exclusion seemed more natural than either locking out the object until all answers have been delivered or providing no mutual exclusion at all.

An example

As an example, consider the object declaration for a travel agency. \lprog{agency}{ .ds agency.pl } A travel agency may be asked for a destination. The destinations an agency offers are contained in a list of cities. The non-logical variable cities storing this list is initialized to contain as possible destinations amsterdam, paris and london. Creating a new agency, and subsequently asking it for a destination, is done as in the following goal.
  	?-
  		O = new(agency()),
  		O!destination(Y),
  		...
  
When evaluating the first component of this goal the logical variable O will become bound to the newly created agency that offers as destinations amsterdam, paris and london. Immediately thereafter the method call O!destination(Y) will be evaluated, but the call will not be accepted until the accept statement expressing the willingness to accept a call is reached. The evaluation of the method call will result in binding Y to amsterdam and when backtracking occurs, subsequently to paris and london. Then the call will fail. Backtracking over a new statement is not possible. This call will simply fail. \nop{

And parallelism

As an additional primitive DLP offers an and-parallel operator. The goal ?- O!destination(tokyo) & O!add(tokyo) will result in concurrently requesting the object O to check for the destination tokyo and to add tokyo as a destination. Due to the mutual exclusion between method calls, since an agency is an active object, one of the calls will be accepted first and exclude the other until it has produced a result. So the goal above may either fail or succeed, depending on whether the request for adding a destination is granted first. }

Inheritance

An essential feature of the object oriented approach is the use of inheritance to define the relations between objects. C.f.  [We87],  [WZ88],  [HOB87]. Inheritance may be conveniently used to factor out the code common to a number of objects. For an untyped language, as DLP, inheritance is a facility to share code. The declaration for an object inheriting from an object base is \dlpindex{inheritance} \oprog{inheritance}{
    object name : base {
    var variables.
    clauses
    }
  
} This declaration will result in adding the non-logical variables and clauses declared for the object base to those of the declared object. Any non-logical variables of the base object that occur also in the declared object will be overwritten by the non-logical variable of the declared object. This will be of significance only when at least one of the non-logical variables has an initializing declaration. Such overwriting does not take place for clauses. When the declared object contains clauses similar to clauses in the base object, concerning the same predicate, then these will be treated as alternative choices for evaluating an atom. As an example, consider the relation between a researcher and a professor, stated in the declaration below. \lprog{researcher}{
   object researcher {
   var field.
  
   knowsof(X):- member(X,field).
   }
  
   object professor : researcher {
  
   knowsof(X):- committee(Conference,X).
  
   }
  
} The intended meaning here is that a professor knows of the topics of the conferences for which he has been a member of the program committee, in addition to what he knows of as a researcher.