Previous Table of Contents Next


11.6.7. Revelations

A revelation introduces information about an opaque type into a scope. Unlike other declarations, revelations introduce no new names.

There are two kinds of revelations, partial and complete. A program can contain any number of partial revelations for an opaque type; it must contain exactly one complete revelation.

A partial revelation has the form

   REVEAL T <: V

where V is a type expression (possibly just a name) and T is an identifier (possibly qualified) declared as an opaque type. It reveals that V is a supertype of T.

In any scope, the revealed supertypes of an opaque type must be ordered linearly by the subtype relation. That is, if it is revealed that T <: U1 and T <: U2, it must also be revealed either that U1 <: U2 or that U2 <: U1.

A complete revelation has the form

   REVEAL T = V

where V is a type expression (not just a name) whose outermost type constructor is a branded reference or object type and T is an identifier (possibly qualified) that has been declared as an opaque type. The revelation specifies that V is the concrete type for T. It is a static error if any type revealed in any scope as a supertype of T is not a supertype of V. Generally, this error is detected at link time.

Distinct opaque types have distinct concrete types because V includes a brand and all brands in a program are distinct.

A revelation is allowed only in an interface or in the outermost scope of a module. A revelation in an interface can be imported into any scope where it is required, as illustrated by the stack example.

For example, consider the following:

   INTERFACE I; TYPE T <: ROOT; PROCEDURE P(x:T): T; END I.
   INTERFACE IClass; IMPORT I; REVEAL I.T <: MUTEX; END IClass.
   INTERFACE IRep; IMPORT I;
     REVEAL I.T = MUTEX BRANDED OBJECT count: INTEGER END;
   END IRep.

An importer of I sees I.T as an opaque subtype of ROOT and is limited to allocating objects of type I.T, passing them to I.P, or declaring subtypes of I.T. An importer of IClass sees that every I.T is a MUTEX and can therefore lock objects of type I.T. Finally, an importer of IRep sees the concrete type and can access the count field.

11.6.8. Recursive Declarations

A constant, type, or procedure declaration N = E, a variable declaration N: E, an exception declaration N(E), or a revelation N = E is recursive if N occurs in any partial expansion of E. A variable declaration N := I where the type is omitted is recursive if N occurs in any partial expansion of the type E of I. Such declarations are allowed if every occurrence of N in any partial expansion of E is within some occurrence of the type constructor REF or PROCEDURE, within a field or method type of the type constructor OBJECT, or within a procedure body.

Examples of legal recursive declarations:

   TYPE
     List = REF RECORD x: REAL; link: List END;
     T = PROCEDURE(n: INTEGER; p: T);
     XList = X OBJECT link: XList END;
   CONST N = BYTESIZE(REF ARRAY [0..N] OF REAL);
   PROCEDURE P(b: BOOLEAN) = BEGIN IF b THEN P(NOT b) END END P;
   EXCEPTION E(PROCEDURE () RAISES {E});
   VAR v: REF ARRAY [0..BYTESIZE(v)] OF INTEGER;

Examples of illegal recursive declarations:

   TYPE
     T = RECORD x: T END;
     U = OBJECT METHODS m() := U.m END;
   CONST N = N+1;
   REVEAL I.T = I.T BRANDED OBJECT END;
   VAR v := P(); PROCEDURE P(): ARRAY [0..LAST(v)] OF T;

Examples of legal non-recursive declarations:

   VAR n := BITSIZE(n);
   REVEAL T <: T;

11.7. Modules and Interfaces

Art, it seems to me, should simplify. That, indeed, is very nearly the whole of the higher artistic process; finding what conventions of form and what detail one can do without and yet preserve the spirit of the whole.

Willa Cather

A module is like a block, except for the visibility of names. An entity is visible in a block if it is declared in the block or in some enclosing block; an entity is visible in a module if it is declared in the module or in an interface that is imported or exported by the module.

An interface is a group of declarations. Declarations in interfaces are the same as in blocks, except that any variable initializations must be constant, and procedure declarations must specify only the signature, not the body.

A module X exports an interface I to supply bodies for one or more of the procedures declared in the interface. A module or interface X imports an interface I to make the entities declared in I visible in X.

A program is a collection of modules and interfaces that contains every interface imported or exported by any of its modules or interfaces and in which no procedure, module, or interface is multiply defined. The effect of executing a program is to execute the bodies of each of its modules. The order of execution of the modules is constrained by the initialization rule.

The module whose body is executed last is called the main module. Implementations are expected to provide a way to specify the main module, in case the initialization rule does not determine it uniquely. The recommended rule is that the main module be the one that exports the interface Main, whose contents are implementation dependent.

Program execution terminates when the body of the main module terminates, even if concurrent threads of control are still executing.

The names of the modules and interfaces of a program are called global names. The method for looking up global names—for example, by file system search paths—is implementation dependent.


Previous Table of Contents Next