This section contains more complete examples of abstract data types realized in Generic Modula-2. The design work for these modules was done in VDM-SL (Vienna Documentation Method-Specification Language) by Matt Suderman for an undergraduate thesis submitted in partial fulfillment of a B.Sc. degree at Trinity Western University in the Spring of 1997. Only a general discussion and the end result in Generic Modula-2 is give here.

## 16.7.1 Generic Lists

For this example, the ADT List has been chosen. The generic aspects are the type of the data and a procedure to do assignments. In order to make a close fit with the design notation, a type has been introduced to model the Natural Numbers, that is, the positive whole numbers (not including zero). This is used to number the list. In addition, take note of the systematic use of preconditions and the provision of procedures to check those preconditions. As usual, various procedures are expressed in terms of the formal parameter names whose values are yet to be supplied.

```GENERIC DEFINITION MODULE Lists (Data: TYPE; AssignData: AssignProcType);

TYPE
AssignProcType = PROCEDURE (VAR Data; Data);
(* ``Nat'' means ``Natural Number'' as in VDM-SL *)
Nat1 = [1 .. MAX (CARDINAL)];
Nat = CARDINAL;
Traversal = PROCEDURE (Data): Data;
List;

PROCEDURE InitList (VAR list: List);
(* Post: Empty (list) = TRUE *)

PROCEDURE CanInsert (list: List; position: Nat1): BOOLEAN;
(* Pre:  'memory available' AND position <= Length (list) + 1 *)
PROCEDURE Insert (VAR list: List; data: Data; position: Nat1);
(* Post: GetLength (list') + 1 = GetLength (list)
AND GetElement (list', 1..position - 1)
= GetElement (list, 1..position - 1)
AND GetElement (list', position..GetLength (list'))
= GetElement (list, position + 1..GetLength (list))
AND GetElement (list, position) = data *)

PROCEDURE CanUpdate (list: List; position: Nat1): BOOLEAN;
(* Pre: position <= Length (list) *)
PROCEDURE Update (VAR list: List; data: Data; position: Nat1);
(* Post: GetLength (list') = GetLength (list)
AND p <> position
==> GetElement (list', p) = GetElement (list, p)
AND GetElement (list, position) = data *)

PROCEDURE CanAppend (list: List; data: Data): BOOLEAN;
(* Pre: 'memory available' AND NOT Empty (list) *)
PROCEDURE Append (VAR list: List; data: Data);
(* Post:  GetLength (list') + 1 = GetLength (list)
AND GetElement (GetLength (list)) = data
AND GetElement (list', 1..GetLength (list'))
= GetLength (list, 1..GetLength (list')) *)

PROCEDURE CanDelete (list: List; position: Nat1): BOOLEAN;
(* Pre: position <= GetLength (list) *)
PROCEDURE Delete (VAR list: List; position: Nat1);
(* Post: GetLength (list') = GetLength (list) + 1
AND GetElement (list', 1..position - 1)
= GetElement (list, 1..position - 1)
AND  GetElement (list', position + 1..)
= GetElement (list, position ..) *)

PROCEDURE Traverse (VAR list: List; traversal: Traversal);
(* Post: for all p GetElement (list, p)
= traversal (GetElement (list', p)) *)

PROCEDURE GetLength (list: List): Nat;
(* Post: the number of elements in the list *)

PROCEDURE Empty (list: List): BOOLEAN;
(* Post: GetLength (list) = 0  Empty (list) = TRUE *)

PROCEDURE CanGetElement (list: List; position: Nat1): BOOLEAN;
(* Pre: position <= GetLength (list) *)
PROCEDURE GetElement (list: List; position: Nat1): Data;
(* Post: GetElement (list, position) = data at position in the list *)

END Lists.```

The implementation part of this generic separate module supplies the code to make everything happen, again expressed in terms of the formal parameters. The formal design of this module included various preconditions, and implementation of these require that one be able to determine ahead of time whether storage space is available. To meet this requirement, a local module is employed that buffers one storage location. Whenever this is used, another one is requested. The function StorageAvailable can examine the buffered location and determine if a valid one is available, and calls to MYNEW return the location available and attempt to replenish the buffer for the next call. Matt has formulated his list design as an inherently recursive structure. A list points to another list (sometimes called its tail) recursively until there are no more data items.

```GENERIC IMPLEMENTATION MODULE Lists (Data: TYPE; AssignData: AssignProcType);

FROM Storage IMPORT ALLOCATE, DEALLOCATE;

TYPE
NodePtr = POINTER TO Node;
Node = RECORD
data: Data;
next: NodePtr;
END;
List = NodePtr;

MODULE MyStorage;
(* Module ``MyStorage'' is necessary to implement the function
``StorageAvailable''.  ``StorageAvailable'' returns true if
``MYNEW'' will return a new non-NIL pointer to a node; otherwise,
if ``MYNEW'' will return a NIL pointer ``StorageAvailable'' returns
false. In other words, ``StorageAvailable'' returns true if another
``Node'' can be allocated in the system heap and false otherwise. *)

IMPORT ALLOCATE, DEALLOCATE, Node, NodePtr;

EXPORT MYNEW, StorageAvailable;

VAR temp: NodePtr;

PROCEDURE StorageAvailable (): BOOLEAN;
BEGIN
RETURN temp <> NIL;
END StorageAvailable;

PROCEDURE MYNEW (init: Node): NodePtr;
(* return the buffered value, and if it was not NIL, get a new one *)
VAR
new: NodePtr;
BEGIN
IF temp = NIL
THEN
RETURN NIL
ELSE
new := temp;
new^ := init;
NEW (temp);
RETURN new
END
END MYNEW;

BEGIN (* initialize one location in the buffer called temp *)
NEW (temp);
END MyStorage; (* end f local module *)

(* resume declarations in main module *)

PROCEDURE InitList (VAR list: List);
BEGIN
list := NIL;
END InitList;

PROCEDURE MkNode (data: Data; next: NodePtr): Node;
VAR
node: Node;
BEGIN
AssignData (node.data, data);
node.next := next;
RETURN node;
END MkNode;

PROCEDURE IsEmpty (list: List): BOOLEAN;
BEGIN
RETURN list = NIL
END IsEmpty;

PROCEDURE Length (list: List): Nat;
(* observe his formulation of length as a recursive procedure. This may take longer to calculate, but he does not have to store and update a value *)
BEGIN
IF NOT IsEmpty (list) THEN
RETURN 1 + Length (list^.next);
ELSE
RETURN 0;
END;
END Length;

PROCEDURE PtrToNode (list: List; position: Nat1): NodePtr;
(* work along the list until the correct position is found, then return a pointer to the rest of the list *)
BEGIN
IF position > 1
THEN
RETURN PtrToNode (list^.next, position - 1);
ELSE
RETURN list;
END;
END PtrToNode;

(* The next three procedures implement the typical inserting possibilities. *)

PROCEDURE InsertAtBeginning (VAR list: List; data: Data);
VAR
new: NodePtr;
BEGIN
new := MYNEW (MkNode (data, list));
list := new;
END InsertAtBeginning;

PROCEDURE InsertAfter (ptr: NodePtr; data: Data);
VAR
new: NodePtr;
BEGIN
new := MYNEW (MkNode (data, ptr^.next));
ptr^.next := new;
END InsertAfter;

(* This one is the exported procedure and must select one of the above. *)

PROCEDURE Insert (VAR list: List; data: Data; position: Nat1);
BEGIN
IF position = 1
THEN
InsertAtBeginning (list, data);
ELSE
InsertAfter (PtrToNode (list, position - 1), data);
END;
END Insert;

PROCEDURE CanInsert (list: List; position: Nat1): BOOLEAN;
BEGIN
RETURN (position <= Length (list) + 1) AND StorageAvailable ();
END CanInsert;

PROCEDURE Append (VAR list: List; data: Data);
VAR
ptr: NodePtr;
BEGIN
ptr := list;
IF ptr = NIL
THEN
InsertAtBeginning (list, data);
ELSE
WHILE ptr^.next <> NIL
DO
ptr := ptr^.next;
END;
InsertAfter (ptr, data);
END;
END Append;

PROCEDURE CanAppend (): BOOLEAN;
BEGIN
RETURN StorageAvailable ();
END CanAppend;

PROCEDURE Update (VAR list: List; data: Data; position: Nat1);
VAR
ptr: NodePtr;
BEGIN
ptr := PtrToNode (list, position);
AssignData (ptr^.data, data);
END Update;

PROCEDURE CanUpdate (list: List; position: Nat1): BOOLEAN;
BEGIN
RETURN (position <= Length (list)) AND StorageAvailable ();
END CanUpdate;

(* The next three procedures implement the typical deleting possibilities. *)

PROCEDURE DeleteAtBeginning (VAR list: List);
VAR
temp: NodePtr;
BEGIN
temp := list;
list := list^.next;
DISPOSE (temp);
END DeleteAtBeginning;

PROCEDURE DeleteAfter (ptr: NodePtr);
VAR
temp: NodePtr;
BEGIN
temp := ptr^.next;
ptr^.next := temp^.next;
DISPOSE (temp);
END DeleteAfter;

(* This one is the exported procedure and must select one of the above. *)

PROCEDURE Delete (VAR list: List; position: Nat1);
BEGIN
IF position = 1
THEN
DeleteAtBeginning (list);
ELSE
DeleteAfter (PtrToNode (list, position - 1));
END;
END Delete;

PROCEDURE CanDelete (list: List; position: Nat1): BOOLEAN;
BEGIN
RETURN position <= Length (list);
END CanDelete;

PROCEDURE Traverse (VAR list: List; traversal: Traversal);
VAR
ptr: NodePtr;
BEGIN
ptr := list;
WHILE ptr <> NIL
DO
AssignData (ptr^.data, traversal (ptr^.data));
ptr := ptr^.next;
END;
END Traverse;

PROCEDURE GetLength (list: List): Nat;
BEGIN
RETURN Length (list);
END GetLength;

PROCEDURE Empty (list: List): BOOLEAN;
BEGIN
RETURN IsEmpty (list);
END Empty;

PROCEDURE GetElement (list: List; position: Nat1): Data;
VAR
ptr: NodePtr;
BEGIN
ptr := PtrToNode (list, position);
RETURN ptr^.data;
END GetElement;

PROCEDURE CanGetElement (list: List; position: Nat1): BOOLEAN;
BEGIN
RETURN position <= Length (list);
END CanGetElement;

END List.```

### 16.7.2 Generic Queues

Here, Matt provides a generic definition module for the type Queue. Implementation is fairly simple and can be based directly on the List module in the previous subsection.

```GENERIC DEFINITION MODULE Queues (Data: TYPE; AssignData: List.AssignProcType);

TYPE
Queue;
Nat = CARDINAL;

PROCEDURE InitQueue (VAR q: Queue);
(* Post: Empty (q) = TRUE *)

PROCEDURE CanEnqueue (q: Queue; d: Data): BOOLEAN;
(* Pre: 'memory available' *)
PROCEDURE Enqueue (VAR q: Queue; d: Data);
(* Post: the Length (q) time, Dequeue (q) is called, Dequeue (q) = d *)

PROCEDURE CanDequeue (q: Queue): BOOLEAN;
(* Pre: NOT Empty (q) *)
PROCEDURE Dequeue (VAR q: Queue): Data;
(* Post: Dequeue (q') = GetHead (q') *)

(* Pre: NOT Empty (q) *)
(* Post: GetHead (q) = the oldest enqueued element *)

PROCEDURE Length (q: Queue): Nat;
(* Post: Length (q) = the number of elements enqueued - number dequeued *)

PROCEDURE Empty (q: Queue): BOOLEAN;
(* Post: Length (q) = 0  Empty (q) = TRUE *)

END Queues.```

### 16.7.3 Generic Stacks

Finally, he gives a generic definition module for the type Stack. Once again, implementation is fairly simple and is left to the student as an exercise.

```GENERIC DEFINITION MODULE Stacks (Data: TYPE; AssignData: List.AssignProcType);

TYPE
Stack;
Nat = CARDINAL;

PROCEDURE InitStack (VAR s: Stack);
(* Post: Empty (s) = TRUE *)

PROCEDURE CanPush (s: Stack; d: Data): BOOLEAN;
(* Pre: 'memory available' *)
PROCEDURE Push (VAR s: Stack; d: Data);
(* Post: Pop (s) = d *)

PROCEDURE CanPop (s:Stack): BOOLEAN;
(* Pre: NOT Empty (s) *)
PROCEDURE Pop (VAR s:Stack): Data;
(* Post: Pop (s') = GetTop (s') *)

PROCEDURE CanGetTop (s: Stack): BOOLEAN;
(* Pre: NOT Empty (s) *)
PROCEDURE GetTop (s: Stack);
(* Post: GetTop (s) = the list element to be pushed *)

PROCEDURE Length (s: Stack): Nat;
(* Post: Length (s) = the number of elements pushed - number popped *)

PROCEDURE Empty (s: Stack): BOOLEAN;
(* Post: Length (s) = 0  Empty (s) *)

END Stacks.```

Contents