#lang scheme ; NOTE: you must use hte langugae level 'module', ; rather than advanced-student, to get the var-args syntax: (define (test-language-level a b . otherArgs) (length otherArgs)) (define seed 1) (define MAX-RAND 123) (define (next-rand-v1) (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND)) seed)) (define (set-seed!-v1 new-val) (set! seed new-val)) ;;;;;;;;;;;;;;;; ;; A version with 'seed' being 'private'. (define two-rng-funcs (let* {[seed 1] [MAX-RAND 123] [next-rand (lambda () (begin (set! seed (remainder (+ (* 23 seed) 17) MAX-RAND)) seed))] [set-seed! (lambda (new-val) (set! seed new-val))]} (list next-rand set-seed!))) (define next-rand-v2 (first two-rng-funcs)) (define set-seed!-v2 (second two-rng-funcs)) ;;; Note: the above is common enough that scheme provides 'match-define': ;;; (match-define (list next-rand-v2 set-seed!-v2) two-rng-funcs) ;;;;;;;;;;;;;; ;; A version where we can make ;; *multiple* pairs-of-functions-which-share-a-local-`seed`. (define (rng-factory) (let* {[sseed 1] [MAX-RAND 123] [next-rand (lambda () (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND)) sseed))] [set-seed! (lambda (new-val) (set! sseed new-val))]} (list next-rand set-seed!))) (match-define (list next-rand-v3a set-seed!-v3a) (rng-factory)) (match-define (list next-rand-v3b set-seed!-v3b) (rng-factory)) ;;;;;;;;;;;;;;;;;; ;; A version where instead of returning a list-of-functions, ;; we return one "meta function" which dispatches to the function that is being asked for: ;; (define (new-rng) (let* {[sseed 1] [MAX-RAND 123] [next-rand (lambda () (begin (set! sseed (remainder (+ (* 23 sseed) 17) MAX-RAND)) sseed))] [set-seed! (lambda (new-val) (set! sseed new-val))] } (lambda (msg . other-args) (cond [(symbol=? msg 'next) (apply next-rand other-args)] [(symbol=? msg 'seed!) (apply set-seed! other-args)] [else (error 'rng (format "No such method recognized: ~a" msg))])))) ;;;;;;;;;;;;;;; ;;; A sub-class (define (new-super-rng) (let* {[super (new-rng)] ; The superclass object. [name "hello"]} ; A new field, only in the subclass. (lambda (msg . other-args) (cond [(symbol=? msg 'skip) 43] [(symbol=? msg 'next) (begin (super 'seed! 17) (super 'next))] #;[(symbol=? msg 'get-seed) sseed] ; This is what we *want* to return, but it'd be an error: sseed ; is in super's scope, but not ours! ; Our approach to implementing an object system can do most things, ; but it can't emulate Java's 'protected' access. [else (apply super msg other-args)])))) ;;; Exercise: our methodology for faking objects (by using closures and ;;; a 'dispatcher' function does allow for calling superclass methods ;;; (through a variable we conveniently named `super`). ;;; However, how could we call methods in the *same* class? ;;; Hint: include the dispatcher method inside the let*, ;;; perhaps naming it `this`. ;;; But 'let*' won't quite work; what let-like construct will you need? "v1:" (next-rand-v1) (next-rand-v1) (next-rand-v1) (set-seed!-v1 1) (next-rand-v1) (next-rand-v1) "v2:" (next-rand-v2) (next-rand-v2) (next-rand-v2) (set-seed!-v2 1) (next-rand-v2) (next-rand-v2) "v3a:" (next-rand-v3a) (next-rand-v3a) (next-rand-v3a) (set-seed!-v3a 1) (next-rand-v3a) (next-rand-v3a) "v3b:" (next-rand-v3b) (next-rand-v3b) (next-rand-v3b) (set-seed!-v3b 1) (next-rand-v3b) (next-rand-v3b) "v4a: (objects)" (define r (new-rng)) (define s (new-rng)) (r 'next) (r 'next) (r 'next) (r 'seed! 1) (r 'next) (r 'next) "v4b:" (s 'next) (s 'next) (s 'next) (s 'seed! 1) (s 'next) (s 'next) "v5 (subclassing)" (define ss (new-super-rng)) (ss 'next) (ss 'next) (ss 'skip) (ss 'next)