4.13 Exercise 4.13

I believe that unbinding variables is already a bad idea, and would not choose to include the feature. I am also inclined to believe that allowing variables in parent environments to be unbound is an even more dangerous idea – imagine if a binding in a closure was destroyed somewhere far away in a way that you didn’t expect. If you were relying on this behavior, it may be very tricky to discover what exactly has gone wrong.

On the other hand, we need to be careful with restricting functionality simply because we believe it is dangerous. Even it actually being dangerous is not necessarily an argument for disallowing it. There is a tradeoff between preventing the unexpected or unwanted and allowing the unforeseen but necessary.

For now, I’m going to make the decision to only allow unbinding variables in the current frame, even though I have misgivings about it.

There is a bit of subtlety in defining make-unbound!, because we need to modify vars and vals such that the binding is question is no longer present. This is easy to do if the list has more than one element in it, but if there is only one then we are stuck – no use of set-car! and set-cdr! can turn this actual pair into nil. So, we instead perform iterate through such that the current variable is the second in the list, and seed the initial lists with dummy head elements to allow for all of the bindings to be deleted. In other words, the base case is now a one-element list, not an empty one. Unfortunately, this means we can’t use search-env from the last exercise.

(define (make-unbound! var env)
  (define (scan vars vals)
    (cond ((null? (cdr vars))
           (error "Cannot unbind non-bound variable" var))
          ((eq? var (cadr vars))
           (begin
             (set-cdr! vars (cddr vars))
             (set-cdr! vals (cddr vals))))
          (else (scan (cdr vars) (cdr vals)))))
  (if (eq? env the-empty-environment)
      (error "Cannot unbind non-bound variable" var)
      (let ((frame (first-frame env)))
        (scan (cons 'vars (frame-variables frame))
              (cons 'vals (frame-values frame))))))