Making Data Abstractions

Interface and Implementation

Often, you will find that Racket, or whatever other language you are using, does not provide a native data type that you need. Whenever you encounter this kind of situation, you know just what to do: create an abstract data type. These abstractions aren't "just" data abstractions, but they also are not "just" syntactic abstractions. Instead, they are behavioral abstractions that involve both data and functions.

Making data abstractions is something you studied in some detail in your Data Structures course. There, you may have spoken of "abstract data types", or ADTs. One of the essential ideas underlying ADTs was that the interface of the ADT is independent of its implementation. The interface specifies a set of values and a set of operations on these values. It is "abstract" in the sense that the interface makes no commitment to how these values and operations are actually implemented. The implementation provides a concrete representation, at least concrete in the sense that it is expressed in terms of some other executable type(s).

The value of ADTs lies in their independence from representation. They allow us to write client code in terms of an interface, for which we can provide a suitable implementation that meets a particular set of needs.


Often, at the beginning of a project, we will choose a simple implementation that allows us to write client code as quickly as possible. Later, we can choose a more efficient implementation, or an implementation that better suits the program's environment, without having to change the client code.

Data Abstraction and Programming Language

This tells us something about data abstraction from the programmer's point of view, but what about from the programming language's point of view?

It turns out that data abstractions play a role in the language similar to the one played by syntactic abstractions. A language with a small set of primitive data types can be interpreted by a simpler program than one that offers a larger set of primitive types. But having too small a set of types makes programming in the language too inconvenient, so we might want to add one or more data abstractions for the programmer's benefit. These abstractions can be implemented in terms of the more fundamental types and thus interpreted by the simpler program.

Racket lists and pairs are a great example. The pair is more primitive because it has the simpler set of values and operations. But Racket also offers a list data type that has a straightforward implementation in terms of pairs, and it allows us to use pairs (and lists) to implement other sorts of data abstractions.