# SICP 7b Metacircular Evaluation II

Metacircular interpreters are convenient for exploring language issues and excellent for exchanging ideas about language design.

## 1 Indefinite number of arguments

One potentially helpful feature to add to a language, is allowing procedures to take an indefinite number of arguments.

```(+ 3 5 7 5 3)
```

To add this feature, we require a syntactic specification to notate the additional arguments.

```(lambda (x . y)
(map (lambda (u) (+ x u))
y))
```

`.`: syntax for representing `cons`​es.

Note the syntactic difference between these three expressions.

```(lambda (x y z) ...)
(lambda (x . y) ...)
(lambda x x)
```

Note `(lambda x x)` is equivalent to `list`.

Now incorporate this feature into the interpreter. For this we need to change `pair-up`.

```(define pair-up
(lambda (vars vals)
(cond
((eq? vars '())
(cond ((eq? vals '()) '())
(else (error "too many arguments"))))
((symbol? vars)               ;new
(cons (cons vars vals) '())) ;new
((eq? vals '()) (error "too few arguments"))
(else
(cons (cons (car vars)
(cdr vals))
(pair-up (cdr vars)
(cdr vals)))))))
```

## 2 Dynamic binding of variables

Next, we will attempt a more substational variation: dynamic binding of variables. First, we'll observe why it may be useful to have.

```(define sum
(lambda (term a next b)
(cond ((> a b) 0)
(else
(+ (term a)
(sum term
(next a)
next
b))))))
(define sum-powers
(lambda (a b n)
(sum (lambda (x) (expt x n))
a
1+
b)))
(define product-powers
(lambda (a b n)
(product (lambda (x) (expt x n))
a
1+
b)))
```

Notice that `sum-powers` and `product-powers` are essentially the same, which is a sure indicator something is awry.

```(define sum-powers
(lambda (a b n)
(sum nth-power a 1+ b)))
(define product-powers
(lambda (a b n)
(product nth-power a 1+ b)))
(define nth-power
(lambda (x)
(expt x n)))
```

Upon first glance, it appears this is preferable. However this is currently impossible, as `n` remains undefined.

Dynamic binding: a free variable in a procedure has its value defined in the chain of callers, rather than where the procedure is defined.

```(define eval
(lambda (exp env)
(cond ((number? exp) exp)
((symbol? exp) (lookup exp env))
((eq? (car exp) 'quote) (cadr exp))
((eq? (car exp) 'lambda) exp)   ;new
((eq? (car exp) 'cond)
(evcond (cdr exp) env))
(else (apply (eval (car exp) env)
(evlist (cdr exp) env)
env)))))           ;new
```

We no longer have to save the environment upon definition of the procedure so no closure is required. However, the environment is required upon the application of the procedure.

```(define apply
(lambda (proc args env) ;new
(cond
((primitive? proc)
(apply-primop proc args))
((eq? (car proc) 'lambda)
;; proc = (LAMBDA bvis body)
args
env))) ;new
(else error-unknown-procedure))))
```

The catch is that dynamic binding leads to a modularity crisis. For example, if `next` in `sum` were to be renamed `n`, then `sum-powers` would stop working. Dynamic binding removes the ability to have qualifiers.

So what would be the correct solution? How do we gain the power of dynamic binding within a lexical system?

```(define pgen
(lambda (n)
(lambda (x) (expt x n))))

(define sum-pairs
(lambda (a b n)
(sum (pgen n) a 1+ b)))
(define product-pairs
(lambda (a b n)
(product (pgen n) a 1+ b)))
```

## 3 Delayed parameters

Finally, we will add the ability to delay the calculation of parameters.

```(define (unless p c a)
(cond ((not p) c)
(else a)))
```

Without delayed parameters, the following code can't be run, as evaluating the arguments leads to an error.

```(unless (= 1 0) 2 (/ 1 0))
```

What we would like instead, is for the following procedure to be constructed.

```(cond ((not (= 1 0)) 2)
(else (/ 1 0)))
```

To construct this, build a kluge.

```(define (unless p (name c) (name a))
(cond ((not p) c)
(else a)))
```

Adding support for delayed parameters requires a far more complicated modification of the interpreter compared to adding both of the previous features.

```(define eval
(lambda (exp env)
(cond ((number? exp) exp)
((symbol? exp) (lookup exp env))
((eq? (car exp) 'quote) (cadr exp))
((eq? (car exp) 'lambda)
(list 'closure (cdr exp) env))
((eq? (car exp) 'cond)
(evcond (cdr exp) env))
(else
(apply (undelay (eval (car exp) ;new
env))
(cdr exp)
env)))))

(define apply
(lambda (proc args env) ;new
(cond
((primitive? proc)
(apply-primop proc
(evlist ops env))) ;new
((eq? (car proc) 'closure)
;; proc = (CLOSURE (bvrs body) env
ops
env)
(else error-unknown-procedure))))

(define evlist
(lambda (l env)
(cond
((eq? l '()) '())
(else
(cons (undelay (eval (car l) env))
(evlist (cdr l) env))))))

(define gevlist
(lambda (vars exps env)
(cond
((eq? exps '()) '())
((symbol? (car vars)) ;applicative
(cons (eval (car exps) env)
(gevlist (cdr vars)
(cdr exps)
env)))
((eq? (caar vars) 'name)
(cons (make-delay (car exps) env)
(gevlist (cdr vars)
(cdr exps)
env)))
(else error-unknown-declaration))))

(define evcond
(lambda (clauses env)
(cond
((eq? clauses '()) '()) ;arbitrary
((eq? (caar clauses) 'else)
((false? (undelay
(eval (caar clauses)
env)))
(vcond (cdr clauses) env))
(else

(define false?
(lambda (x) (eq? x nil)))

(define make-delay
(lambda (exp env)
(cons 'thunk (cons exp env))))

(define (undelay v)
(cond ((pair? v)
(cond ((eq? (car v) 'thunk)
(undelay