next up previous
Next: Constraints Up: Classes Previous: Classes

Concrete Definitions

Concrete definitions may be bound to slots via {...}. A special form, <> is used instead to indicate that a slot is bound to a concrete definition only upon construction. It is used only for ``stored'' functional slots that access a single value that must be intialized upon construction.

Concrete slot definitions may contain the following constructs:

Message Sends:
One-way sends of messages to specified recipients.
Procedure Invocations:
Blocking call/return style interaction.
Function Invocations:
Side-effect-free procedural interaction.
Local Invocations:
Internal sequential processing.
Assignment statements rebind non- fixed stored slots to different values via f := exp.
local declarations of new transient slots maintained only within an operation.
Control flow:
if and while statements controlled by boolean value expressions.

Message Sends

Computation is based on message passing. Objects are autonomous single-threaded computational agents that send messages listed within operations corresponding to messages from other objects. A message is a record that corresponds to a declared operation. ODL op declarations define records with names identical to operation names, and fields corresponding to argument lists. There are five phases in any act of message passing:

An invocation listed in the concrete definition of a sender is issued.

ODL does not specify the mechanisms controlling how messages are issued and received by objects except in the assumption that they do not interfere with explicitly defined processing. Synchronicity between sending and receiving a message is neither required nor precluded.

A message is linked with a corresponding operation or version of an operation. Linkage may be determined either statically or dynamically. (Run-time binding is necessary when there are argument-based guard conditions.) Binding failures cannot occur in correct programs.

An accepted message is ``consumed'', and causes the triggering of an operation when its guard has cleared.

Computation proceeds by noninterruptibly processing all actions defined in the corresponding concrete operation.

The simplest form of a concrete operation is a sequence of one-way message sends, for example, { a.m1(x); b.m2(y, z); ... }. Invocations are defined by by naming them and providing field values along with a recipient designation. Instead of recipient prefixing, ODL messages may also be invoked with an indication of a class of receivers, via className $ message. Stylistically, this form is useful for stateless services for which the identity of the recipient cannot matter. The run-time system is free to select any instance of the indicated class (or any subclass thereof) to receive the message. ODL does not prescribe a particular translation mechanism. Several are available. For example, because all occurrences of $ are statically determinable before execution, the system may construct a pool of such objects upon initialization and translate all invocations to normal prefixed form.


In the base syntax of procedural interaction, result-bearing operations define message records for returned values, and clients define corresponding operations to accept them:

class A op m1(x: T) : result(b: B) ... end end;

class Auser ...
  op calla(a: A) {
    catch a.m1(x) 
      op result(b: B) { b.m2(y, z); ... }
    end  }

Here, the sender enters a state in which its only next action is to receive a corresponding reply. A catch clause introduces one or more transiently available operations that accept replies from servers. Multiple named result messages and catches are also allowed. The names of the client operations must match those listed for the server. Translators must arrange that result operations be caught only when objects are in the required state.

A server object invokes these transient messages by name:

class A op m1(x: T) : result(b: B) { ...; result(new B); } end;

The recipient of the reply is left implicit. This logically requires that sender identity be transmitted as an implicit argument in all procedural messages. However, in ODL the sender identity is not otherwise accessible to the server. (Unless, of course, a sender field is explicitly added to the operation signature and used in the desired ways.) A server may perform additional actions after issuing a reply.

More conventional looking procedural forms are also supported, via anonymous return messages. For example:

  op a : i: int;
  op b : ()

This declares the result of a as an anonymous record with single field i and the result of b as an anonymous, fieldless record. Each anonymous record is considered to have a different name. Anonymous return messages are sent via reply:

class A2 op m1(x: T) : B {  ...; reply b; } end;

class Auser  ...
  op calla2(a: A2) { local b :B := a.m1(x); b.m2(y, z);  }

Here, the anonymous catch may be elided, and the reply used directly in a procedural fashion.

Simple blocking procedural interaction is the only two-way protocol natively supported in ODL. Others may be defined through combinations of one-way sends and object constructions. For example, a future may be defined via the construction of a helper object to wait out a procedure.


Functional operations have a restricted form. They are defined as single expressions using the value expression sublanguage described in section 3.2.3. Translators are required to transform functional expressions into procedural computations (possibly involving new independent, unreachable objects) that cannot interfere with other operations and objects.

Stored functions are yet further restricted. They may be defined only via <>, indicating that a stored value be attached upon construction, retrieved upon access, and possibly rebound in the course of other operations. Stored links may also be qualified as packed. This is a hint to the translator that the object referenced by the slot should be embedded within the representation of the host object.

The base form of stored values is restricted to link values, not other types. The reflects an underlying object model in which state varies only as a function of connections among objects. Other forms may be implemented with the help of instances of elementary predefined classes. However, ODL programmers are not required to do so themselves. Translators may mechanically reduce them to base form. A stored value denoting a non-link type may be translated to one holding a link to an instance of a predefined class, where value accesses are forwarded to these objects. Value rebindings may be translated either to link rebindings of new objects with the required initial values or to set operations on the exisiting objects, or any other technique, at the discretion of the translator. Bindings of the form l := null for opt slots are handled similarly.

Local Operations

ODL local operation invocations are not received as messages. They are sequentially executed in the course of performing other actions. To avoid the need for redundant declaration, a local version of each functional non-local operation is automatically constructed if not otherwise present. Local operations must be invoked without a recipient prefix. (In contrast non-local self-invocations must be prefixed with self.)

Local functions and procedures may in turn invoke others, and may be recursive. Standard procedural invocation rules and semantics apply. One-way local operations are also allowed. Invocations are interpreted as structured ``gotos'' in which control does not return to the calling operation. For this reason, procedural operations may not invoke local one-ways.

The execution state of objects is in general unbounded. The existence and value of representational bounds for particular classes and objects may be conservatively assessed via static analysis of local operations. When bounds are not discerned or discernable, ODL implementations may establish maximal per-object run-time size limits and handle overflow as a run-time error.


Every instantiable class declaration automatically results in the definition of a corresponding new operation in class System. The new operation has arguments corresponding to all slots declared as <>, and returns a unique link value referencing an object of the indicated class. Implementations of new (as well as delete) are not definable within ODL, although provision of implementation-specific blob-based classes and object layout rules may make them so.

Without qualification, the class's new operation may be invoked anywhere. The visibility of new may be controlled via a generator clause in a class declaration. A generator clause names the classes of entities that may invoke new for the class.


ODL message passing rules assume preservation of referential integrity. Objects that may still receive messages may not be deleted. This is best implemented using automatic storage management (garbage collection). However, a delete operation is also associated with each concrete class. Visibility is also controlled via generator.

next up previous
Next: Constraints Up: Classes Previous: Classes

Doug Lea@Sat Apr 8 10:25:42 EDT 1995