;; Caltech CS1 Fall 2007
;; Scheme code used in Lecture 14 (11/14/07)
;; mvanier@cs.caltech.edu

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Rules for environment model:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

1) 'define' creates a new binding in the current frame
   (first frame in the current environment).

2) 'set!' modifies a binding in the current environment
   (in the first frame the binding is found in), but doesn't create
   new bindings.

3) Evaluating a lambda expression (procedure) creates a pair containing:
   -- the text of the procedure (formal parameters, code)
   -- a pointer to the environment where the lambda was evaluated

4) Applying a lambda expression (procedure) to its arguments
   -- creates a new frame where the formal parameter names are
      bound to the (evaluated) arguments to the procedure
      -- and where the parent frame of the new frame is the frame 
         pointed to by the lambda pair
   -- evaluates the code of the procedure body in the context of
      the environment corresponding to the new frame

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; day 13 contrast
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define astate 0)
(define (accum! x)
  (begin
    (set! astate (+ astate x))
    astate))
(accum! 1) ; astate = 1
(accum! 1) ; astate = 2
(accum! 1) ; astate = 3

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (make-accum value)
  (lambda (x) 
    (set! value (+ value x))
    value))
;; use
(define a! (make-accum 0))
(define b! (make-accum 0))
(a! 1) ; --> 1
(a! 1) ; --> 2
(a! 1) ; --> 3
(b! 1) ; --> 1
(a! 1) ; --> 4
(b! 1) ; --> 2
(a! 1) ; --> 5
;; Note that each procedure has its own "value" variable
;; that changes independently of the other.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Problems with global state
;; (note shared astate with accum!)
(set! astate 0) ; reset astate to 0
(define (toggle)
  (if (= 0 astate) 
      (set! astate 1)
      (set! astate 0)))

(accum! 1) ; --> astate = 1
(toggle)   ; --> astate = 0
(accum! 1) ; --> astate = 1
(accum! 1) ; --> astate = 2
(toggle)   ; --> astate = 0
(accum! 1) ; --> astate = 1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Multiple procedures which share internal variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Message passing version
(define (mp-make-accum value)
  (lambda (op . args)
    (cond ((eq? op 'accum)
           (set! value (+ value (car args))))
          ((eq? op 'value) value)
          ((eq? op 'reset) (set! value 0))
          (else 
           (error "unknown op: " op)))))

(define a2 (mp-make-accum 0))
(a2 'value)
((a2 'accum) 1) ;; note different message passing syntax
;; Previously, we would have written this as: (a2 'accum 1)
;; but because (a2 'accum) returns a lambda expression, here
;; we have to write the full function call as ((a2 'accum) 1).
(a2 'value) 
(a2 'accum 1)
(a2 'reset)
(a2 'value)

;; A different way to write this:
(define (mp-make-accum2 value)
  (lambda (op)
    (cond ((eq? op 'accum)
           (lambda (x) (set! value (+ value x)))
          ((eq? op 'value) value)
          ((eq? op 'reset) (set! value 0))
          (else 
           (error "unknown op: " op)))))

;; Usage:
(define a3 (mp-make-accum2 0))
(a3 'value)      ;; same as above
((a3 'accum) 1)  ;; different
(a3 'reset)      ;; same as above
