## COS 441 - Procedural Abstraction and Syntactic Abstraction - Feb 15, 1996

### Induction, Recursion, Programming

Parse trees, *lists, and lists are inductively defined structures. To operate over an inductively defined structure, write a recursive procedure that examines each of definition. We have seen several examples of such procedures already.

Let's take a look at the natural numbers defined inductively.

```N ::= 0 | succ N

(define even?
(lambda (n)
(cond ((zero? n) #t)
(else (odd? (- n 1))))))

(define odd ...)
```
Repeated patterns in programming are bad because each repetition offers another opportunity to introduce a bug. Use procedures to abstract common patterns. Consider the problem of adding one to each element of a list.
```(define add-each
(lambda (l)
(cond ((null? l) '())
```
Suppose we also need to subtract one from every element.
```(define sub-each
(lambda (l)
(cond ((null? l) '())
(else (cons (sub1 (car l)) (add-each (cdr l)))))))
```
These procedures are nearly the same. We can write one procedure, abstracting over the operation:
```(define each
(lambda (f l)
(cond ((null? l) '())
(else (cons (f (car l)) (each (cdl l)))))))
```
This procedure is map.

Now consider multiplying all the numbers of a list together.

```(define *-overlist
(lambda (l)
(if (null? l) 1
(* (car l) (*-overlist (cdr l))))))
```
If we also wanted to define `+-overlist` we should use procedure abstraction. To do this we write a folding function. Folding can occur either to the left or to the right. The procedure above folds to the right.
```(define foldr
(lambda (f i l)
(if (null? l) i
(f (car l) (foldr f i (cdr l))))))

(define *-overlist
(lambda (l)
(foldr * 1 l)))
```
`foldr` multiplies elements from right to left. We can also build a fold function that works left to right.
```(define foldl
(lambda (f i l)
(if (null? l)
i
(foldl f (f i (car l)) (cdr l)))))
```
The direction of the fold does not matter in the case of a communicative operation. But if the function is not communicative or has side effects the direction of the fold is quite important. Note how we can now define `map` using `foldr`.
```(map f l) = (foldr (lambda (a b) (cons (f a) b)) '() l)
```
Much of this we can also do in C, but suppose we can define a function that adds n to its parameter in the following manner.
```(define add-n
(lambda (n)
(lambda (m)
(+ m n))))

(map (add-n 3) '(1 2 3))
```
This example shows that we can construct open procedures that capture the value of bound variables. You can not do this in C, hence procedures like `map` and `fold` are less useful in C.

### Syntactic Abstraction

Convince yourself that the following are equivalent.

```(lambda (x y)
(let ((x y)
(y x))
x)))

(lambda (x y)
((lambda (x y) x)
y
x))
```
This example shows that `let` is a syntactic abstraction. In general
```  (let ((x[1] e[1]) ... (x[n] e[n])) e')
= ((lambda(x[1] .. x[n]) e') e[1] ... e[n])
```
The reason that `let` is not a function is that it manipulates bindings. Why else might we want to have syntactic abstractions? Consider the following.
```  (cond ((test then[1] ... then[n]) clause*))
= (if test (begin then[1] ... then[n]) (cond clause*)))

(cond (else e[1] ... e[n]))
= (begin e[1] ... e[n])

(cond)
= undef
```
Here we see the second motivation for syntactic abstraction: to change argument evaluation order. Now lets look at `and` and `or`.
```  (and e[0])
= e[0]

(and e[0] ... e[n])
= (if (e[0]) (and (e[1] ... e[n]) #f))

(or e[0])
= e[0]

(or e[0] ... e[n])
= (let ((x e[0])) (if x x (or e[1] ... e[n])))     x not in FV(e[1] ... e[n])
```
Scheme has short-circuit evaluation like C. Suppose that we forget the condition that x not be in FV(e[1] ... e[n]).
```(define x 1)
(or #f x)
```
This should be 1, but instead it is expanded into
```(let ((x #f)) (if x x x))
```
What happened? variable `x` was captured by the binding introduced by the macro. We must have the hygiene condition that x not in FV(e[1] ... e[n]) to prevent this. We usually want hygiene for all variable bindings introduced by a macro. Hygienic macro systems do this automatically. The following shows that we have the same problem in C.
```#define swap(x,y) { int temp = x; x = y; y = temp; }
...
swap(temp,x);
```

You can define macros in our Scheme system using `extend-syntax`. There you will find some examples of how to use this feature. Although the extend-syntax system we are using is not hygienic, we can get a little bit of help from `gensym`. It generates a unique symbol within the context of an `extend-syntax` expression. The following is an outline for the definition of `or`.

```(extend-syntax (or)
((or e) e)
((or e1 e2 ... )
(with ((x (gensym)))
(let ((x e1)
(if ... ))))))
```

### Exercises

• Derive map from foldl.
• Define cond, or, and using extend-syntax.