(define (fm-make-posn x0 y0) (local ((define x y0) (define y y0) (define (service-manager msg) (cond [(symbol=? msg 'x) x] [(symbol=? msg 'y) y] [(symbol=? msg 'set-x) (lambda (x-new) (set! x x-new))] [(symbol=? msg 'set-y) (lambda (y-new) (set! y y-new))] [else (error 'posn ``...'')]))) service-manager))(define (fm-posn-x p) (p 'x))
(define (fm-posn-y p) (p 'y))
(define (fm-set-posn-x! p new-value) ((p 'set-x) new-value))
(define (fm-set-posn-y! p new-value) ((p 'set-y) new-value))
Together, sections
and
suggest that structures are mutable. That is, we
should be able to change the values of some field in a structure. After
all, we introduced the service managers in section
to
hide state variables, not just ordinary variable definitions.
Figure
shows how a small change to the
definitions of figure
turns the locally
hidden variables into state variables. The modified service manager offers
two services per state variable: one for looking up the current value and
one for changing it.
Consider the following definition and expression:
(define a-posn (fm-make-posn 3 4))Evaluating them by hand shows how structures change. Here is the first step:(begin (fm-set-posn-x! a-posn 5) (+ (posn-x a-posn) 8))
...
=
(define x-for-a-posn 3)
(define y-for-a-posn 4)
(define (service-manager-for-a-posn msg)
(cond
[(symbol=? msg 'x) x-for-a-posn]
[(symbol=? msg 'y) y-for-a-posn]
[(symbol=? msg 'set-x)
(lambda (x-new) (set! x-for-a-posn x-new))]
[(symbol=? msg 'set-y)
(lambda (y-new) (set! y-for-a-posn y-new))]
[else (error 'posn ``...'')]))
(define a-posn service-manager-for-a-posn)
(begin
(fm-set-posn-x! a-posn 5)
(+ (posn-x a-posn) 8))
It renames and lifts the local definitions from inside of
fm-make-posn. Because the function definition doesn't change for
the rest of the evaluation, we focus on just the variable definitions:
(define x-for-a-posn 3)
(define y-for-a-posn 4)
(begin
(fm-set-posn-x! a-posn 5)
(+ (posn-x a-posn) 8))
= (define x-for-a-posn 3)
(define y-for-a-posn 4)
(begin
(fm-set-posn-x! service-manager-for-a-posn 5)
(+ (posn-x a-posn) 8))
= (define x-for-a-posn 3)
(define y-for-a-posn 4)
(begin
((service-manager-for-a-posn 'set-x) 5)
(+ (posn-x a-posn) 8))
= (define x-for-a-posn 3)
(define y-for-a-posn 4)
(begin
(set! x-for-a-posn 5)
(+ (posn-x a-posn) 8))
= (define x-for-a-posn 5)
(define y-for-a-posn 4)
(+ (posn-x a-posn) 8)
At this point, the definition of x-for-a-posn has been modified in the expected manner. From now on every reference to this state variable, which represents the (simulated) x field a-posn, stands for 5. Every further reference to x-for-a-posn produces 5.
Exercise 40.2.1
Develop a functional representation for the following structure definition:
(define-struct boyfriend (name hair eyes phone))such that the fields of the simulated structure can be changed. Solution
Exercise 40.2.2
Here is a modification of the function-based implementation of
posn structures in exercise
:
(define (ffm-make-posn x0 y0)
(local ((define x x0)
(define (set-x new-x) (set! x new-x))
(define y y0)
(define (set-y new-y) (set! y new-y)))
(lambda (select)
(select x y set-x set-y))))
(define (ffm-posn-x a-ffm-posn)
(a-ffm-posn (lambda (x y sx sy) x)))
(define (ffm-posn-y a-ffm-posn)
(a-ffm-posn (lambda (x y sx sy) y)))
(define (ffm-set-posn-x! a-ffm-posn new-value)
(a-ffm-posn (lambda (x y sx sy) (sx new-value))))
(define (ffm-set-posn-y! a-ffm-posn new-value)
(a-ffm-posn (lambda (x y sx sy) (sy new-value))))
Demonstrate how to modify a structure like (ffm-make-posn 3 4) so
that its y field contains 5. Solution