Building Abstractions with Data
2.1 Introduction to Data Abstraction
2.1.3 What Is Meant by Data?
We began the rational-number implementation inSection 2.1.1by im-plementing the rational-number operationsadd-rat,sub-rat, and so on in terms of three unspecified procedures:make-rat,numer, anddenom. At that point, we could think of the operations as being defined in terms of data objects—numerators, denominators, and rational numbers—whose behavior was specified by the laer three procedures.
But exactly what is meant by data? It is not enough to say “whatever is implemented by the given selectors and constructors.” Clearly, not every arbitrary set of three procedures can serve as an appropriate basis for the rational-number implementation. We need to guarantee that, if we construct a rational numberxfrom a pair of integersnandd, then extracting thenumerand thedenomofxand dividing them should yield the same result as dividingn by d. In other words, make-rat,numer, anddenommust satisfy the condition that, for any integer n and any
non-zero integerd, if xis(make-rat n d), then
(numer x) (denom x) = n
d.
In fact, this is the only conditionmake-rat,numer, anddenommust fulfill in order to form a suitable basis for a rational-number representation.
In general, we can think of data as defined by some collection of se-lectors and constructors, together with specified conditions that these procedures must fulfill in order to be a valid representation.5
is point of view can serve to define not only “high-level” data ob-jects, such as rational numbers, but lower-level objects as well. Consider the notion of a pair, which we used in order to define our rational num-bers. We never actually said what a pair was, only that the language supplied procedurescons,car, andcdrfor operating on pairs. But the only thing we need to know about these three operations is that if we glue two objects together usingconswe can retrieve the objects using
carandcdr. at is, the operations satisfy the condition that, for any objectsxandy, ifzis(cons x y)then(car z)isxand(cdr z)isy.
5Surprisingly, this idea is very difficult to formulate rigorously. ere are two ap-proaches to giving such a formulation. One, pioneered by C. A. R.Hoare (1972), is known as the method of abstract models. It formalizes the “procedures plus conditions”
specification as outlined in the rational-number example above. Note that the condi-tion on the racondi-tional-number representacondi-tion was stated in terms of facts about integers (equality and division). In general, abstract models define new kinds of data objects in terms of previously defined types of data objects. Assertions about data objects can therefore be checked by reducing them to assertions about previously defined data ob-jects. Another approach, introduced by Zilles at , by Goguen, atcher, Wagner, and Wright at (seeatcher et al. 1978), and by Guag at Toronto (seeGuag 1977), is called algebraic specification. It regards the “procedures” as elements of an abstract alge-braic system whose behavior is specified by axioms that correspond to our “conditions,”
and uses the techniques of abstract algebra to check assertions about data objects. Both methods are surveyed in the paper byLiskov and Zilles (1975).
Indeed, we mentioned that these three procedures are included as prim-itives in our language. However, any triple of procedures that satisfies the above condition can be used as the basis for implementing pairs.
is point is illustrated strikingly by the fact that we could implement
cons,car, and cdr without using any data structures at all but only using procedures. Here are the definitions:
(define (cons x y) (define (dispatch m)
(cond ((= m 0) x) ((= m 1) y)
(else (error "Argument not 0 or 1: CONS" m)))) dispatch)
(define (car z) (z 0)) (define (cdr z) (z 1))
is use of procedures corresponds to nothing like our intuitive notion of what data should be. Nevertheless, all we need to do to show that this is a valid way to represent pairs is to verify that these procedures satisfy the condition given above.
e subtle point to notice is that the value returned by(cons x y)is a procedure—namely the internally defined proceduredispatch, which takes one argument and returns eitherxorydepending on whether the argument is 0 or 1. Correspondingly,(car z)is defined to applyzto 0.
Hence, ifzis the procedure formed by(cons x y), thenzapplied to 0 will yieldx. us, we have shown that(car (cons x y))yieldsx, as desired. Similarly,(cdr (cons x y))applies the procedure returned by
(cons x y)to 1, which returnsy. erefore, this procedural implemen-tation of pairs is a valid implemenimplemen-tation, and if we access pairs using onlycons,car, andcdrwe cannot distinguish this implementation from one that uses “real” data structures.
e point of exhibiting the procedural representation of pairs is not
that our language works this way (Scheme, and Lisp systems in general, implement pairs directly, for efficiency reasons) but that it could work this way. e procedural representation, although obscure, is a perfectly adequate way to represent pairs, since it fulfills the only conditions that pairs need to fulfill. is example also demonstrates that the ability to manipulate procedures as objects automatically provides the ability to represent compound data. is may seem a curiosity now, but procedu-ral representations of data will play a centprocedu-ral role in our programming repertoire. is style of programming is oen called message passing, and we will be using it as a basic tool inChapter 3when we address the issues of modeling and simulation.
Exercise 2.4:Here is an alternative procedural representa-tion of pairs. For this representarepresenta-tion, verify that(car (cons
x y))yieldsxfor any objectsxandy.
(define (cons x y) (lambda (m) (m x y))) (define (car z)
(z (lambda (p q) p)))
What is the corresponding definition ofcdr? (Hint: To ver-ify that this works, make use of the substitution model of Section 1.1.5.)
Exercise 2.5:Show that we can represent pairs of nonneg-ative integers using only numbers and arithmetic opera-tions if we represent the pair a and b as the integer that is the product 2a3b. Give the corresponding definitions of the procedurescons,car, andcdr.
Exercise 2.6:In case representing pairs as procedures wasn’t mind-boggling enough, consider that, in a language that can manipulate procedures, we can get by without numbers (at least insofar as nonnegative integers are concerned) by implementing 0 and the operation of adding 1 as
(define zero (lambda (f) (lambda (x) x))) (define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
is representation is known as Church numerals, aer its inventor, Alonzo Church, the logician who invented the λ-calculus.
Defineoneandtwodirectly (not in terms ofzeroand
add-1). (Hint: Use substitution to evaluate(add-1 zero)). Give a direct definition of the addition procedure+(not in terms of repeated application ofadd-1).