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-support@elego.de