Chapter 3: Modularity, Objects, and State
so far we have reviewed the primitive elements of how programs are
made and how those primitive elements can be combined in complex
ways.
we learned the value of abstraction and modular programs.
- modular
- the nature of a program thot it can be divided
"naturally" into coherent parts that can be separately
developed and maintained
we now look towards how to design entire systems. we will look at two
prominent strategies for program construction:
- objects
- seeing a program as a collection of distinct objects
whose behaviors may change over time - streams
- seeing a program as a flow of information in a system
we will finally discard the substitution model of chapter 1 in favor
of the environment model
3.1 assignment and local state
we ordinarily view the world as a collection of independent objects
each of which has state that changes over time.
at this point (in ch 3!!!) we will discuss local state variables and
the assignment operator
3.1.1 local state variables
we now have a comptational object with time-varying state
this means:
(withdraw 25) --> 75 (withdraw 25) --> 25 (withdraw 60) --> "Insufficient Funds" (withdraw 15) --> 35
these are not pure funtions
when evaluated twice, they yield different results
this is new before, we computed mathematical functions
(define balance 100) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient Funds"))
we have the new language features, set! which sets a new value to a
variable:
(set! name new-value)
and we have the (begin … … …) form, which returns the value of
its final expression
there's some trouble with that begin variable. we can make it
internal by encapsulating it:
(define new-withdraw (let ((balance 100)) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient Funds"))))
combining set! with local variables is the general technique for
constructing computational objects.
–> here we have just annihilated our substitution model. according to
it, the above is incomprehensible. we will address later. first, a
few variations on this new and very exciting theme
(define (make-withdraw balance) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient Funds")))
which can be used to make accounts with custom amounts
(define W1 (make-withdraw 100)) (define W2 (make-withdraw 100)) (W1 50) (W2 70)
these two withdrawal accounts are independent of each other
to make a functional bank account:
(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)
and to use it:
(define acc (make-account 100)) ((acc 'withdraw) 25) ((acc 'withdraw) 80) ((acc 'deposit) 120) ((acc 'withdraw) 60)
and:
(define acc2 (make-account 100))
will be a completely different account
3.1.2 the benefits of introducing assignment
with assignment, we can encapsulate state, rather than injecting it
via formal parameters all over the place.
3.1.3 the costs of introducing assignment
set! is cool, but it breaks the substitution model.
it breaks all "nice" mathematical models.
the reason it doesnt work is variable scope. like closures, i
think. or like the kind of closure this book said is the wrong thing.
- functional programming
- as we did in the first two chapters of
the book, to not use assignment - imperative programming
- makes extensive use of assignment
SAMENESS AND CHANGE
- referentially transparent
- "equals can be substituted for
equals" without changing the value of an expression
set! violates referential transparency.