The Correlate language syntax


Correlate is a concurrent object-oriented language. It is heterogeneous in the sense that it supports two different kinds of objects : active and passive objects. Passive objects are the objects used in normal sequential oo programming languages (like Java) and thus follow the syntactic rules of the host language (in this case Java). Active objects are supported by the Correlate runtime and have additional features. The decision of an object is active of passive depends upon the class.

Active Classes

To declare a class active, use the active modifier. Interfaces can be declared active in a similar way. This declaration is orthogonal to both abstractness and accessibility (private/public/package).

public active class Consumer {
  ...
}

active interface Mobile {
   ...
}

Active classes can only extend active classes and implement active interfaces. By default when no superclass is specified, an active class extends CorrelateObject (like a Java class extends Object).

When an instance of an active class needs to refer to itself, it can do so through the self keyword. This is just like the "this" keyword in Java.

Defining operations

Correlate supports three different kinds of operations on active classes.

  1. By default, an operation is external. This means that this operation is an external interface operation that can be invoked by other objects.
  2. An operation can be declared autonomous by the autonomous modifier. Autonomous operations are invoked automatically by the execution environment. Once completed, they will be reinvoked until the object is destroyed. The return type of an autonomous operation is void; neither parameters nor exceptions are allowed.
  3. When an operation is declared internal, it is only accessible to the object itself. Even objects of the same class can't invoke it.

In addition, a Correlate class can define a number of constructors and a destructor. When no constructor is declared, a public default constructor is automatically generated. The rules for constructor chaining (calling the constructor of the superclass) are just as in Java. The name of the destructor is finalize.

Each of these operation can be given any number of modifier such as supported by Java. The semantics of these modifiers are like in Java. For instance, a private operation can only be invoked by objects of the same class and a operation declared final cannot be overwritten.

Examples:

   final autonomous protected void step() {  ... }
   public Producer createProducer(int i) { ... }
   internal private boolean readyToCompute() { ... }

Synchronisation

All operations (with the exception of those declared internal and constructors) defined on an active class can be given a synchronization precondition. As long as the precondition evaluates to false, all invocations of this operation on the object in case will be blocked.

Syntactically it is coded like a declaration of thrown exceptions. The precondition expression is evaluated in a scope that contains the parameters of the invocation. A derived class can redefine the implementation of an operation, the synchronization code or both.

Example:

void push(Object o)
      precondition !isFull(o) 
      throws BufferIsLockedException 
{ ... }

Object Interaction

Object creation and deletion

Active objects are not automatically garbage collected. An explicit invocation of the delete operator is required to deallocate them. A synchronisation modifier indicates if the creation (resp. destruction) should be executed asynchronously (@) or synchronously ($).

Examples:

Car myNewCar = new @ Car(john,belgium);
Dog myNewDog = new $ Dog(``Felix'');
...
delete $ myNewDog;

Message passing

Correlate uses a ``@'' for asynchronous interaction and ``$'' for synchronous interaction. Correlate only supports replies for synchronous messages. The return value or the possible thrown exception of an asynchronous invocation is discarded by the run time.

When an object makes a call upon itself, it can choose whether it wants synchronisation or not. When the object uses the CORRELATE syntax (@) the invocation is synchronized, Java syntax happens unsynchronized.

Examples:

buffer @ put(item);            // asynchronous
buffer $ put(item);            // synchronous
item = buffer $ get();         // synch with reply
item = buffer @ get();         // NONSENSE ! Item is undefined after this assignment.

d = internalFunction(a,b,c);   // unsynchronized

self @ someOperation(a,b);     // synchronized, but asynchronous
self $ someOperation(a,b);     // object becomes deadlocked

Parameterbindingmechanisms

Passive objects are passed by value, active objects by reference. For performance reasons however, the runtime only copies passive objects when moving from one VM to another. It is the responsibility of the application programmer to ensure that no passive objects are shared between two or more active objects.

Complete Example

This is a complete example of an autonomous Producer object that sends 10 messages to a simple Consumer object.

public active class Producer {
  int count;
  Consumer consumer;

  Producer() { // default constructor
    System.out.println("Creating new Producer object.");
    consumer = new @ Consumer();
    count = 0;
  }

  autonomous void produce() precondition count < 10 {
    count++;
    try {
      consumer $ consume((int) (Math.random() * 10000));
    } catch (IAmFullException e) {
      System.out.println("Oops, overproduction !");
    }
  }
}
active class Consumer {
  private int count;

  public Consumer() {
    count = 1;
  }

  public void finalize() { // destructor !
    super.finalize();
    System.out.println("Consumer " + self + " destroyed.");
  }

  public void consume(int p) throws IAmFullException {
    System.out.println("Consumer " + self + " consumes " + p);
    count++;
    if (count > 8) throw new IAmFullException();
  }
}