All PL types are high-level since the details of how the values of the type are represented are abstracted away. For regular types these details are taken care of by the compiler. For ADTs, these details are given by the programmer who creates the type. In both cases, a programmer who uses the type does not need to know the implementation details.
Formally: An ADT has two parts: a specification part and an implementation part. A specification has two parts: a signature and constraints. An implementation has two parts: a concrete type and a signature implementation.
Informally:
The implementation part of an ADT is "private": only the implementer
needs to know the details of exactly how values are represented, and how
the basic operations on values are accomplished.
This example uses ML syntax, but the ideas are applicable to any PL. First here's a signature:
signature pqsig = sigNext here is a sketch of appropriate constraints on the functions introduced in the signature:
type pq
val onil: pq
val ohd: pq -> int
val otl: pq -> pq
val null: pq -> bool
val insert: int -> pq -> pq
val sort: int list -> pq
end;
null nil = true;Of course many more equations are also true about relationships between the operations named in the signature.
forall i:int x:pq null (insert i x) = false
forall i,j:int x:pq insert(i,insert(j,x)) = insert(j,insert(i,x))
...
One obvious concrete representation for priority queues is as unordered lists. Here's an appropriate (incomplete) implementation:
structure pqstruct : pqsig = structNotice that the sort function is implemented as a "no-op". This implies that the ohd and otl functions must have non-trivial implementations.
datatype pq = wrap of int listfun doinsert (n: int) [] = [n]
| doinsert n (a::x) = if n <= a then n::(a::x)
else a::(doinsert n x)
val onil = (wrap [])
fun ohd (wrap (a::x)) = ...
fun otl (wrap (a::x)) = ...
fun null (wrap []) = true
| null (wrap (a::x)) = false
fun insert n (wrap x) = wrap (doinsert n x)
fun sort x = (wrap x)
end
Important note: The semantic part is conceptually important but programming
languages typically do not require it. There are two reasons for this:
(1) verifying that an implementation actually satisfies a specification
is an unsolved problem in artificial intelligence, and (2) the semantics
of specification languages is an open question.
The implementation part of an ADT is "private": only the implementer needs to know the details of exactly how values are represented, and how the basic operations on values are accomplished.
Languages have various mechanisms for making ADT function names visible. For example in ML:
- pqstruct.onil;
val it = wrap [] : pqstruct.pq
- open pqstruct;
- onil;
val it = wrap [] : pq