Read Exercise 4.13 ~ Solution ~ Test suite
I decided to change the implementation of frames for this exercise back to using a pair of lists, rather than a list of pairs. I also decided that make-unbound
can only unbind variables in the same lexical scope. It seems dangerous to me to be able to unbind a more global variable from within a nested scope – if the variable is referenced anywhere else outside of the nested scope the program is going to raise unexpected errors. It’s bad enough dealing with mutation of values without having to deal with definitions completely vanishing.
Luckily we defined an abstraction to deal with frames and so it’s a relatively painless to change.
(define (make-frame variables values) (cons variables values)) (define (frame-binding var frame) (let scan ((variables (car frame)) (values (cdr frame))) (cond ((or (null? variables) (null? values)) 'frame-binding-missing) ((eq? var (car variables)) (car values)) (else (scan (cdr variables) (cdr values)))))) (define (bound? var frame) (memq var (car frame))) (define (add-binding-to-frame! var val frame) (set-car! frame (cons var (car frame))) (set-cdr! frame (cons val (cdr frame)))) (define (set-binding-in-frame! var val frame) (let scan ((variables (car frame)) (values (cdr frame))) (cond ((or (null? variables) (null? values)) 'frame-binding-missing) ((eq? var (car variables)) (set-car! values val)) (else (scan (cdr variables) (cdr values))))))
To unbind a variable from a frame
(define (remove-binding-from-frame! var frame) (let scan ((variables (car frame)) (values (cdr frame))) (cond ((or (null? variables) (null? values)) (error "Unbound variable -- REMOVE-BINDING-FROM-FRAME!" var)) ((eq? var (car variables)) (set-car! variables '()) (set-car! values '())) (else (scan (cdr variables) (cdr values)))))) (define variable-to-unbind cadr) (define (eval-make-unbound! exp env) (remove-binding-from-frame! (variable-to-unbind exp) (first-frame env))) (put-syntax! 'make-unbound! eval-make-unbound!)