Internal Definitions

SICP > Modularity, Objects, and State > The Environment Model of Evaluation > Internal Definitions
Previous: Frames as the Repository of Local State Next: Modeling with Mutable Data

    Section [*] introduced the idea that procedures can have internal definitions, thus leading to a block structure as in the following procedure to compute square roots:

    (define (sqrt x)
      (define (good-enough? guess)
        (< (abs (- (square guess) x)) 0.001))
      (define (improve guess)
        (average guess (/ x guess)))
      (define (sqrt-iter guess)
        (if (good-enough? guess)
            guess
            (sqrt-iter (improve guess))))
      (sqrt-iter 1.0))
    
    Now we can use the environment model to see why these internal definitions behave as desired. Figure [*] shows the point in the evaluation of the expression (sqrt 2) where the internal procedure good-enough? has been called for the first time with guess equal to 1.


      \begin{figure}\par\figcaption {{\tt Sqrt} procedure with internal definitions.}\end{figure}

    Observe the structure of the environment. Sqrt is a symbol in the global environment that is bound to a procedure object whose associated environment is the global environment. When sqrt was called, a new environment E1 was formed, subordinate to the global environment, in which the parameter x is bound to 2. The body of sqrt was then evaluated in E1. Since the first expression in the body of sqrt is

    (define (good-enough? guess)
      (< (abs (- (square guess) x)) 0.001))
    
    evaluating this expression defined the procedure good-enough? in the environment E1. To be more precise, the symbol good-enough? was added to the first frame of E1, bound to a procedure object whose associated environment is E1. Similarly, improve and sqrt-iter were defined as procedures in E1. For conciseness, figure [*] shows only the procedure object for good-enough?.

    After the local procedures were defined, the expression (sqrt-iter 1.0) was evaluated, still in environment E1. So the procedure object bound to sqrt-iter in E1 was called with 1 as an argument. This created an environment E2 in which guess, the parameter of sqrt-iter, is bound to 1. Sqrt-iter in turn called good-enough? with the value of guess (from E2) as the argument for good-enough?. This set up another environment, E3, in which guess (the parameter of good-enough?) is bound to 1. Although sqrt-iter and good-enough? both have a parameter named guess, these are two distinct local variables located in different frames. Also, E2 and E3 both have E1 as their enclosing environment, because the sqrt-iter and good-enough? procedures both have E1 as their environment part. One consequence of this is that the symbol x that appears in the body of good-enough? will reference the binding of x that appears in E1, namely the value of x with which the original sqrt procedure was called.

    The environment model thus explains the two key properties that make local procedure definitions a useful technique for modularizing programs:

    Exercise. In section [*] we saw how the environment model described the behavior of procedures with local state. Now we have seen how internal definitions work. A typical message-passing procedure contains both of these aspects. Consider the bank account procedure of section [*]:

    (define (make-account balance)
      (define (withdraw amount)
        (if (>= balance amount)
            (begin (set! balance (- balance amount))
                   balance)
            "Insufficient funds"))
      (define (deposit amount)
        (set! balance (+ balance amount))
        balance)
      (define (dispatch m)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
              (else (error "Unknown request - MAKE-ACCOUNT"
                           m))))
      dispatch)
    
    Show the environment structure generated by the sequence of interactions

    (define acc (make-account 50))
    
    

    ((acc 'deposit) 40) 90

    ((acc 'withdraw) 60) 30

    Where is the local state for acc kept? Suppose we define another account

    (define acc2 (make-account 100))
    
    How are the local states for the two accounts kept distinct? Which parts of the environment structure are shared between acc and acc2?  

    Previous: Frames as the Repository of Local State Next: Modeling with Mutable Data

      webmaster@arsdigita.org