It is beneficial to separate the concepts of "specification" and "implementation" of an ADT.

- The specification is the abstract description of the type and the behaviour of the operations. It should be implementation-independent and even language-independent. It should give the user all the information necessary to use the ADT, but no more.
- The implementation, on the other hand, is the concrete representation of the elements of the new type in terms of existing types, and the definition of the operations as functios or procedures. The implementation should of course satisfy the specification, i.e. the behaviour of the operations, their types, etc. should be those prescribed by the specification.

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.

- the creation of the empty list, i.e. the list with 0 nodes (emptylist),
- the test whether a list is empty (is_empty),
- the addition of a new node in front of a list (cons),
- the access to the information in the first node (head),
- the list obtained by removing the first node (tail).

- f: () -> T means that f has 0 arguments and result of type T.
- f: (T1) -> T2 means that f has 1 argument of type T1 and result of type T2.
- f: (T1) -> T2 means that f has 2 arguments, of type T1 and T2 respectively, and result of type T.
- ... etc.

- emptylist: () -> list
- is_empty: (list) -> boolean
- cons: (integer,list) -> list
- head: (list) -> integer
- tail: (list) -> list

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.

- concrete representation of the elements of the new type in terms of existing types,
- definition of the operations as functions or procedures

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.