It is beneficial to separate the concepts of "specification" and "implementation" of an ADT.
Some languages, like Pascal Standard and C, allow the definition of new types, but do not provide any mechanism for data protection. Namely, there is no way to "shelter" the implementation, i.e. to forbid the user to access the ADT via the operations allowed on the implementation. This practice, of course, violates the principle of the ADT and nullifies the advantages mentioned above. More modern languages, like Modula 2 and C++, have introduced mechanisms for hiding the implementation and making it externally inaccessible (See the book of Sethi, Ch. 6).
We illustrate the concept of ADT by showing the specification and implementation of the type "simple list of integers" in a Pascal-like language.
We might add to this specification the property that lists are non-circular structures, i.e. should contain no loop.
Actually, real specifications of ADT's should give the specification of the algebra, i.e. the semantics of the operations, in a more detailed and formal way than our abstract description.. Furthermore, a good specification should include any property that might be relevant for the programmer. However, we won't go in details about formal methods for ADT specifications, since it would be out of the scope of this course.
type list = ^element;
element = record
info : integer;
next = list
end;
function emptylist : list;
begin
emptylist := nil
end;
function is_empty(L:list): boolean;
begin
if L = nil then is_empty := true else is_empty := false
end;
function cons(x:integer, L:list): list;
var aux : list;
begin
new(aux);
aux^.info := x;
aux^.next := L;
cons := aux
end;
function head(L:list): integer;
begin
if L = nil
then head := 0 /* error */
else head := L^.info
end;
function tail(L:list): list;
begin
if L = nil
then tail := nil /* error */
else begin
tail := L^.next;
dispose(L) /* We might want to eliminate this instruction */
end /* dispose(L), because it causes side effects */
end; /* on other lists sharing L. */
Note that the property that lists are non-circular structures is satisfied by this implementation: it is not possible to create circular lists by using only the functions above.
A correct use of the ADT should use only the operations of the interface, i.e. emptylist, isempty, cons, head and tail. For instance, consider below two possible definitions of the append function: The first respect this principle, the second doesn't. Of course, if the language offered a mechanism to protect the ADT, the second could not even be written.
function append(L1,L2:list): list
begin
if is_empty(L1)
then append := L2
else append := cons(head(L1),append(tail(L1),L2))
end;
function append(L1,L2:list): list
begin
if L1 = nil
then append := L2
else append := cons(L1^.info,append(L1^.next, L2))
end;
Note that the second definition is not implementation-independent:
if we change the implementation of lists, then the second definition
will not be valid anymore. The first one, on the contrary, will
still be valid, provided of course
that the new implementation respects the specification.