f[d -> r](x) = { r if d = x, f(x) otherwiseLet'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.

(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.

`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`