Search code examples
schemesicp

Simulate a bank balance withdraw behavior with lambda


I am reading 3.1 Assignment and Local State of SICP

(define balance 100)
(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance) ;
      "Insufficient funds"))
;Value: balance

1 ]=> 
;Value: withdraw
1 ]=> (withdraw 50)
;Value: 50
1 ]=> (withdraw 30)
;Value: 20

This solution presents a problem with exposure balance state, as a solution, introduce local variable

(define new-withdraw
  (let ((balance 100))
    (lambda (amount) ;;
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

> (new-withdraw 40)
;Value: 60

lambdais introduced as well, even thought it could be rephrased as

(define (new-withdraw-2 amount)
  (let ((balance 100))
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds")))

;Value: new-withdraw-2
8 error> (new-withdraw-2 30)
;Value: 70

I assumed that applying lambda here seems not necessary. What I missed?


Solution

  • The third function is very different from the second one, since in it every time you make a withdraw you compute as result value 100 - amount (the parameter of the function), while in the second one there is an initial balance, 100, and each time that you make a withdraw, it is detracted from the current balance.

    (define new-withdraw
      (let ((balance 100))
        (lambda (amount) ;;
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds"))))
    
    > (new-withdraw 40)
    ;Value: 60
    
    > (new-withdraw 30)
    ;Value: 30           ; equal to 60 - 30
    
    
    (define (new-withdraw-2 amount)
      (let ((balance 100))
          (if (>= balance amount)
              (begin (set! balance (- balance amount))
                     balance)
              "Insufficient funds")))
    
    > (new-withdraw-2 40)
    ;Value: 60
    
    >(new-withdraw-2 30)
    ;Value: 70   ; equal to 100 - 30
    

    So, new-withdraw-2 does not model a bank account, and the function is just an elaborate way of defining f (x) = 100 - x.

    Why these two functions differ so much, even if they seems similar at a superficial glance?

    The difference is given by the semantics of the let: in the first case, in new-withdraw the let introduces a new variable balance, which establishes a new “environment”, and then return a new function (the inner lambda), for which the variable balance is external. So the returned function, each time that it is called, accesses the same variable and decreases its value. The next time it will be called, it will found, through the same variable, the value decreased in the previous call. Objects such as the inner function are called “closures”, as the function and is external environment are strictly connected, with the environment forming a state “hidden” in the function, and persistent between the different calls of the function.

    In the second case, instead, the let is inside the function new-withdraw-2: this means that each time the function is called, a new variable balance is defined, which establishes a new environment, local to the function, the variabile is initialized to 100, and then it is decreased (with set!). But when the function is terminated, returning the new balance, the local environment of the function is lost, and the next time the function will be called a new environment will be established again, with the variabile balance initialized again to 100.