## COS 441 - Data Abstraction and the First Order Transformation - Feb 20, 1996

### Finite Functions

A finite function is a function with finite domain:
```f[d -> r](x) = { r if d = x,
f(x) otherwise
```
Let's implement finite functions in Scheme:
```(define make-empty
(lambda ()
(lambda(x) #f)))

(define extend
(lambda (f d r)
(lambda (x)
(if (eq? x d) r (f x)))))

(define lookup
(lambda (f x)
(f x)))
```
We need `make-empty` because we're using an inductive definition of finite functions and we need a base case.

The implementation of `extend` simply follows the mathematical definition above.

The reason that we use `lookup` rather than explicitly invoke `f` is to provide abstraction over the representation of finite functions. By using `lookup`, any client code that uses this abstraction will know nothing about the underlying implementation of finite functions as procedures. Therefore we will be free to change or improve our implementation of finite functions without making any changes to client code.

Now let's look at an alternative implementation of finite functions. But rather than pull a rabbit out of a hat, we're going to derive this new implementation from our existing one. This should give us reasonable confidence that the new implementation behaves the same way as the old one.

### The First Order Transformation

We can transform the procedural implementation of finite functions to one that uses records.
```(define-record empty-ff ())
(define-record extend-ff (fun dom ran))

(define make-empty make-empty-ff)
(define extend make-extend-ff)

(define lookup
(lambda (f x)
(variant-case f
(empty-ff () #f)
(extend-ff (fun dom ran) (if (eq? x dom) ran (lookup fun x))))))
```
To perform this transformation, we do the following:
• build a new record at each lambda that contains the values of the lambda's free variables;
• move the action of each lambda to the place(s) where the lambda is applied; and
• replace references to the free variables of the lambda with fields from the record.
In this case, there are two lambda's that represent finite functions in the original implementation. One is returned by `make-empty`; the other is built by `extend`. Hence the two kinds of records `empty-ff` and `extend-ff`.

Moving the actions of the two lambda expressions to where they are applied means moving the lambda bodies

```    #f    and     (if (eq? x d) r (f x))
```
to the application `(f x)` in ```lookup and to the application (f x) in the procedure built by extend. Since there are two different kinds of records that represent finite functions in the new implementation (or two different lambda's that represent finite functions in the old), we need to use variant-case at these two places. The second application (f x) within extend might seem problematic because we need to move the procedure built by extend inside itself. To solve this quandry, we use recursion! Other procedural representations We can use procedural representations for many other kinds of data. For instance, we can represent pairs (i.e., cons, car, cdr) using procedures. A definition of cons follows. (define cons (lambda (x y) (lambda (f) (f x y)))) Exercise Provide implementations of car and cdr such that: (car (cons a b)) = a (cdr (cons a b)) = b ```