Tags SICP

The code for this section is in Scheme.

So, we commence with chapter 3 of the book – Modularity, Objects, and State. The video lecture relevant to this section – 5a was, in my opinion, excellent. The way Sussman modeled assignment as a point in time was very lucid. He also explained the environment model of evaluation quickly and efficiently. Even when all these concepts are long familiar to you, learning a new way to look at things helps a lot. The approach taken in SICP to fully understand how the language works under the hood is very educational.

Exercise 3.1

This is very similar to the bank account example, except for a small twist – we want to be able to provide an initial value for the accumulation. Since this value is accepted by make-accumulator, the correct way to place it is in this function’s argument list.

(define (make-accumulator initial)
  (let ((accumulator initial))
    (lambda (addon)
      (set! accumulator (+ accumulator addon))
      accumulator)))

Note that we don’t need begin here, because the body of a lambda definition is implicitly wrapped in one.

Exercise 3.2

We’ll use the dispatch method employed in the last make-account example:

(define (make-monitored proc)
  (let ((call-count 0))
    (define (dispatch m)
      (cond 
        ((eq? m 'how-many-calls?) call-count)
        ((eq? m 'reset-count) (set! call-count 0))
        (else
          (set! call-count (+ 1 call-count))
          (proc m))))
    dispatch))

Exercise 3.3

The modification is only to dispatch – the rest of the code should know nothing about passwords:

(define (make-account balance password)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch pass m)
    (if (eq? pass password)
      (cond ((eq? m 'withdraw) withdraw)
            ((eq? m 'deposit) deposit)
            (else (error "Unknown request -- MAKE-ACCOUNT" m)))
      (error "Bad password -- " pass)))
  dispatch)

A part of this function may not be obvious, and although the book explains it I want to dwell on this point a little.

The local bindings created by define’s arguments aren’t different from using the let form1. So, password and balance are captured inside the definition of make-account the same way call-count of exercise 3.2 is. When the interpreter executes the definition of make-account, it also executes the definition of dispatch, with an environment that contains balance and password having the values passed to make-account.

Exercise 3.4

(define (make-account balance password)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (let ((bad-pass-count 0))
    (define (dispatch pass m)
      (if (eq? pass password)
        (begin
          (set! bad-pass-count 0)
          (cond ((eq? m 'withdraw) withdraw)
                ((eq? m 'deposit) deposit)
                (else (error "Unknown request -- MAKE-ACCOUNT" m))))
        (begin
          (set! bad-pass-count (+ bad-pass-count 1))
          (when (> bad-pass-count 7)
            (call-the-cops))
          (printf "~a~%" "Bad password")
          (lambda (x) x))))
    dispatch))

The weird combination of printf and (lambda (x) x) in the end of dispatch allows me to signal an error and yet not trigger an exit of the interpreter on first error. It would exit otherwise, since dispatch must return a function in order to work properly.

1 In fact, let can be implemented using lambda.