Search code examples
schemelispconstraintssicp

SICP: Why does process-forget-value call process-new-value?


This code is from SICP, 3.3.5 Propagation of Constraints. I can't seem to figure out why process-forget-value needs to call process-new-value as the final step.

The text says, "The reason for this last step is that one or more connectors may still have a value (that is, a connector may have had a value that was not originally set by the adder), and these values may need to be propagated back through the adder."

What is the simplest constraint network that can demonstrate why (process-new-value) is needed? Thanks!

(define (adder a1 a2 sum)
  (define (process-new-value)
    (cond ((and (has-value? a1) (has-value? a2))
           (set-value! sum
                       (+ (get-value a1) (get-value a2))
                       me))
          ((and (has-value? a1) (has-value? sum))
           (set-value! a2
                       (- (get-value sum) (get-value a1))
                       me))
          ((and (has-value? a2) (has-value? sum))
           (set-value! a1
                       (- (get-value sum) (get-value a2))
                       me))))

  (define (process-forget-value)
    (forget-value! sum me)
    (forget-value! a1 me)
    (forget-value! a2 me)
    (process-new-value))  ;;; * WHY * ???

  (define (me request)
    (cond ((eq? request 'I-have-a-value)  
           (process-new-value))
          ((eq? request 'I-lost-my-value) 
           (process-forget-value))
          (else 
           (error "Unknown request -- ADDER" request))))

  (connect a1 me)
  (connect a2 me)
  (connect sum me)
  me)

Solution

  • This Is a test I have made removing process-new-value from the adder. You will see that the behavior is different.

    (define c (make-connector))
    (define a (make-connector))
    (define b (make-connector))
    (define d (make-connector))
    
    (constant 10 a)
    (constant 10 c)
    (constant 10 d)
    
    (define adder1 (adder a b c))
    (define adder2 (adder a b d))
    
    > (has-value? b)
    #t
    
    > (get-value b)
    0
    
    > (forget-value! b adder1)
    'done    
    
    > (has-value? b)
    #f
    

    If you do this with the correct version.

    > (has-value? b)
    #t
    

    The second time also. As they say, when adder1 tells b to forget its value. a and c being constants will still have a value, and the last process-new-value in adder2, will again set b to 0. This will also work if you use set-value! for a and c.