RU beehive logo ITEC dept promo banner
ITEC 380
2012fall
ibarland
tlewis32

homelecturesexamshwsbreeze (snow day)

lect13a-scope-objects
let over lambda

  1  #lang racket
  2  
  3  
  4  ; NOTE: you must use #lang racket ('language: as determined in source')
  5  ; rather than advanced-student, to get the var-args syntax:
  6  (define (test-language-level a b . otherArgs)
  7    (length otherArgs))
  8  
  9  
 10  
 11  ;;; We implement a random-number generator.
 12  ;;; We need state to do this;
 13  ;;; version 1 will use (and set!) a global variable `seed`.
 14  ;;;
 15  
 16  (define seed 1)
 17  (define MAX-RAND 123)
 18  
 19  (define (next-rand-v1)
 20    (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND))
 21           seed))
 22  
 23  (define (set-seed!-v1 new-val)
 24    (set! seed new-val))
 25  
 26  
 27  
 28  "v1:"
 29  (next-rand-v1)
 30  (next-rand-v1)
 31  (next-rand-v1)
 32  (next-rand-v1)
 33  (next-rand-v1)
 34  "re-setting v1:"
 35  (set-seed!-v1 1)
 36  (next-rand-v1)
 37  (next-rand-v1)
 38  
 39  
 40  ;;;;;;;;;;;;;;;;
 41  ;; v1 is nice, but it has a major problem:
 42  ;; since 'seed' is global, anybody can muss with that variable.
 43  
 44  
 45  ;; v2: a version where `seed` is 'private'.
 46  
 47  (define two-rng-funcs
 48    (let* {[seed 1]
 49           [MAX-RAND 123]
 50           [next-rand (lambda ()
 51                        (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND))
 52                               seed))]
 53           [set-seed! (lambda (new-val) (set! seed new-val))]}
 54      (list next-rand set-seed!)))
 55  
 56  (define next-rand-v2 (first two-rng-funcs))
 57  (define set-seed!-v2 (second two-rng-funcs))
 58  ;;; Note: the above is common enough that scheme provides 'match-define':
 59  ;;;   (match-define (list next-rand-v2 set-seed!-v2) two-rng-funcs)
 60  ;;;
 61  ;;; In python:   (x,y) = (3,4)
 62  ;;;              (nextRandv2, setSeedv2) = two-rng-funcs
 63  
 64  
 65  ;;; DEFINITION: the "closure" of a function is the set of all
 66  ;;; binding which the function can refer to.
 67  ;;; Note that at the top-level,
 68  ;;; the id `next-rand-v2` is in scope,
 69  ;;; the id `seed` is not in scope,
 70  ;;; but it *is* in the function's scope.
 71  ;;; (Put another way: even though we finished eval'ing the let*
 72  ;;; a long time ago, the variable it created might live on inside
 73  ;;; a function's closure, so it can't be garbage collected.
 74  ;;; Hopefully such variables were allocated on the heap,
 75  ;;; not on the stack!)
 76  
 77  
 78  "v2:"
 79  (next-rand-v2)
 80  (next-rand-v2)
 81  (next-rand-v2)
 82  (set-seed!-v2 1)
 83  (next-rand-v2)
 84  (next-rand-v2)
 85  
 86  
 87  
 88  ;;;;;;;;;;;;;;
 89  ;; A version where we can make
 90  ;; *multiple* pairs-of-functions-which-each-share-a-local-`seed`.
 91  
 92  
 93  (define (rng-factory)
 94    (let* {[sseed 1]
 95           [MAX-RAND 123]
 96           [next-rand (lambda ()
 97                        (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND))
 98                               sseed))]
 99           [set-seed! (lambda (new-val) (set! sseed new-val))]}
100      (list next-rand set-seed!)))
101  
102  ;; The only difference in code between v2 and v3:
103  ;; the parens around 'rng-factory'!
104  
105    
106  (match-define (list next-rand-v3a set-seed!-v3a) (rng-factory))
107  (match-define (list next-rand-v3b set-seed!-v3b) (rng-factory))
108  
109  
110  
111  "v3a:"
112  (next-rand-v3a)
113  (next-rand-v3a)
114  (next-rand-v3a)
115  (set-seed!-v3a 1)
116  (next-rand-v3a)
117  "v3b:"
118  (next-rand-v3b)
119  (next-rand-v3b)
120  (next-rand-v3b)
121  (set-seed!-v3b 1)
122  (next-rand-v3b)
123  (next-rand-v3b)
124  
125  "continue using next-rand-v3a"
126  (next-rand-v3a)
127  (next-rand-v3a)
128  
129  
130  ;;;;;;;;;;;;;;;;;;
131  ;; A version where instead of returning a list-of-functions,
132  ;; we return one "meta function" which dispatches to the 
133  ;; function that is being asked for:
134  ;; 
135  (define (new-rng)
136    (let* {[sseed 1]
137           [MAX-RAND 123]
138           [next-rand (lambda ()
139                        (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND))
140                               sseed))]
141           [set-seed! (lambda (new-val) (set! sseed new-val))]
142           }
143      (lambda (msg . other-args)
144        (cond [(symbol=? msg 'next)  (apply next-rand other-args)]
145              [(symbol=? msg 'seed!) (apply set-seed! other-args)]
146              [else (error 'rng (format "No such method recognized: ~a" msg))]))))
147    
148  
149  
150  
151  "v4a: (objects)"
152  (define r (new-rng))
153  (define s (new-rng))
154  (r 'next)
155  (r 'next)
156  (r 'next)
157  (r 'seed! 1)
158  (r 'next)
159  (r 'next)
160  "v4b:"
161  (s 'next)
162  (s 'next)
163  (s 'next)
164  (s 'seed! 1)
165  (s 'next)
166  (s 'next)
167  
168  
169  
170  ;;;;;;;;;;;;;;;;
171  ;;; A sub-class:
172  ;;; "class niftier-rng extends rng":
173  ;;;
174  ;;; We add a new method 'skip' (which advances the seed, but returns nothing useful),
175  ;;; and we override 'next' so that it doubles the superclass's result.
176  
177  
178  (define (new-niftier-rng)
179    (let* {[super (new-rng)]  ; The superclass object.
180           [name "hello"]}    ; A new field, only in the subclass.
181      (lambda (msg . other-args)
182        (cond [(symbol=? msg 'skip) (begin (super 'next) "skipped")]
183              [(symbol=? msg 'next) (* 2 (super 'next))]
184              #;[(symbol=? msg 'get-seed) sseed]
185              ; This is what we *want* to return, but it'd be an error: sseed
186              ; is in super's scope, but not ours!
187              ; Our approach to implementing an object system can do most things,
188              ; but it can't emulate Java's 'protected' access
189              ; (since 'subclassing' this way is something any function can do).
190              [else (apply super msg other-args)]))))
191  
192  
193  ;;; Exercise: our methodology for faking objects (by using closures and
194  ;;; a 'dispatcher' function) does allow for calling superclass methods
195  ;;; (through a variable we conveniently named `super`).
196  ;;; However, how could we call methods in the *same* class?
197  ;;; Hint: include the dispatcher method inside the let*,
198  ;;; perhaps naming it `this`.
199  ;;; But `let*` won't quite work; you'll need `letrec`.
200  
201  
202  
203  
204  "v5 (subclassing)"
205  (define ss (new-niftier-rng))
206  (ss 'next)
207  (ss 'next)
208  (ss 'skip)
209  (ss 'next)
210  (ss 'seed! 1)
211  (ss 'next)
212  
213  
214  
215  #| "Let over lambda":
216  The sandwiching of 'lambda' and 'let' is doing interesting things for us.
217  (And fwiw, recall that let can be re-written in terms of lambda...so
218   lambda alone is enough to implement objects!)
219  
220  Hey, our own language N4 has both 'let' and 'lambda' --
221  that means we can essentially implement subclassing and polymorphism!
222  |#

homelecturesexamshwsbreeze (snow day)


©2012, Ian Barland, Radford University
Last modified 2012.Nov.26 (Mon)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Powered by PLT Scheme