In a generic interface or module, some of the imported interface names are treated as formal parameters, to be bound to actual interfaces when the generic is instantiated.
A generic interface has the form
GENERIC INTERFACE G(F_1, ..., F_n);
Body
END G.
where G is an identifier that names the generic interface,
F_1, ..., F_n is a list of identifiers, called
the formal imports of G, and Body is a sequence of imports
followed by a sequence of declarations, exactly as in a non-generic
interface.
An instance of G has the form
INTERFACE I = G(A_1, ..., A_n) END I.
where I is the name of the instance and A_1, ...,
A_n is a list of actual interfaces to which the formal imports
of G are bound. The instance I is equivalent to an ordinary
interface defined as follows:
INTERFACE I;
IMPORT A_1 AS F_1, ..., A_n AS F_n;
Body
END I.
A generic module has the form
GENERIC MODULE G(F_1, ..., F_n);
Body
END G.
where G is an identifier that names the generic module,
F_1, ..., F_n is a list of identifiers, called
the formal imports of G, and Body is a sequence of imports
followed by a block, exactly as in a non-generic module.
An instance of G has the form
MODULE I EXPORTS E = G(A_1, ..., A_n) END I.
where I is the name of the instance,
E is a list of interfaces exported
by I, and A_1, ...,
A_n is a list of actual interfaces to which the formal
imports of G are bound.
"EXPORTS E" can be omitted, in which case
it defaults to "EXPORTS I".
The instance I is equivalent
to an ordinary module defined as follows:
MODULE I EXPORTS E;
IMPORT A_1 AS F_1, ..., A_n AS F_n;
Body
END I.
Notice that the generic module itself has no exports; they are supplied
only when it is instantiated.
For example, here is a generic stack package:
GENERIC INTERFACE Stack(Elem);
(* where Elem.T is not an open array type. *)
TYPE T <: REFANY;
PROCEDURE Create(): T;
PROCEDURE Push(VAR s: T; x: Elem.T);
PROCEDURE Pop(VAR s: T): Elem.T;
END Stack.
GENERIC MODULE Stack(Elem);
REVEAL
T = BRANDED OBJECT n: INTEGER; a: REF ARRAY OF Elem.T END;
PROCEDURE Create(): T =
BEGIN RETURN NEW(T, n := 0, a := NIL) END Create;
PROCEDURE Push(VAR s: T; x: Elem.T) =
BEGIN
IF s.a = NIL THEN
s.a := NEW(REF ARRAY OF Elem.T, 5)
ELSIF s.n > LAST(s.a^) THEN
WITH temp = NEW(REF ARRAY OF Elem.T, 2 * NUMBER(s.a^)) DO
FOR i := 0 TO LAST(s.a^) DO temp[i] := s.a[i] END;
s.a := temp
END
END;
s.a[s.n] := x;
INC(s.n)
END Push;
PROCEDURE Pop(VAR s: T): Elem.T =
BEGIN DEC(s.n); RETURN s.a[s.n] END Pop;
BEGIN
END Stack.
To instantiate these generics to produce stacks of integers:
INTERFACE Integer; TYPE T = INTEGER; END Integer.
INTERFACE IntStack = Stack(Integer) END IntStack.
MODULE IntStack = Stack(Integer) END IntStack.
Implementations are not expected to share code between different
instances of a generic module, since this will not be possible in
general.
Implementations are not required to typecheck uninstantiated generics, but they must typecheck their instances. For example, if one made the following mistake:
INTERFACE String; TYPE T = ARRAY OF CHAR; END String.
INTERFACE StringStack = Stack(String) END StringStack.
MODULE StringStack = Stack(String) END StringStack.
everything would go well until the last line, when the compiler would
attempt to compile a version of Stack in which the element type
was an open array. It would then complain that the NEW call
in Push does not have enough parameters.
m3-request@src.dec.com
Last modified on Mon Apr 18 14:03:26 PDT 1994 by kalsow