DOC PREVIEW
UW CSE 341 - Lecture 19 Summary

This preview shows page 1-2 out of 5 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 5 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 5 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 5 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

CSE 341, Spring 2008, Lecture 19 SummaryStandard Disclaimer: These comments may prove useful, but certainly are not a complete summary of allthe important stuff we did in class. They may make little sense if you missed class, but hopefully will helpyou organize and process what you have learned.This lecture considers two topics: (1) modularity and abstraction in Scheme (particularly extensions inDrScheme), and (2) general notions of equivalence related to functions.Scheme ModularityIn lecture 11, we first studied module systems. We learned how ML module systems provide namespacemanagement, hide private bindings, and enforce invariants by using abstract types. In this lecture, we willconsider how DrScheme’s define-struct also lets us enforce invariants although the approach can feel a bitdifferent. We will see how to do this “just” with local scope and then more conveniently with DrScheme’smodule system.We will consider the same example we did in lecture 11, an interface for positive rational numbers whoseML signature is:sigtype rationalexception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval print_rat : rational -> unitendmake_frac returns a rational only if its arguments are positive. print_rat always prints rationals in reducedform (i.e., 2/3 instead of 6/9). Internal implementation decisions should not be visible to clients, so wecould change the implementation without clients knowing. Such decisions include (1) the representation ofrationals, (2) the existence of private helper functions, and (3) whether rationals are kept in reduced formor just printed that way.Consider first this approach, which achieves (2), but not (1) or (3):(define-struct rat1 (num den))(define pos-rat-funs1(letrec([gcd (lambda (x y) (cond [(= x y) x][(< x y) (gcd x (- y x))][#t (gcd y x)]))][reduce (lambda (r) (let ([d (gcd (rat1-num r) (rat1-den r))])(make-rat1 (quotient (rat1-num r) d)(quotient (rat1-den r) d))))][make-frac (lambda (x y)(unless (and (integer? x) (> x 0)(integer? y) (> y 0))(error "bad rat"))(reduce (make-rat1 x y)))][add (lambda (r1 r2)(check-rat r1)(check-rat r2)(let ([a (rat1-num r1)][b (rat1-den r1)][c (rat1-num r2)][d (rat1-den r2)])(reduce (make-rat1 (+ (* a d) (* b c))(* b d)))))][print-rat (lambda (r)(check-rat r)1(print (rat1-num r))(unless (= (rat1-den r) 1)(begin (print ’/)(print (rat1-den r)))))][check-rat (lambda (r)(if (not (rat1? r))(error "non-rational provided"))(let ([x (rat1-num r)][y (rat1-den r)])(if (or (not (integer? x))(not (> x 0))(not (integer? y))(not (> y 0))(not (= 1 (gcd x y))))(error "invariants violated"))))])(list make-frac add print-rat)))(define make-frac (car pos-rat-funs1))(define add (cadr pos-rat-funs1))(define print-rat (caddr pos-rat-funs1))Here we use no new features of Scheme. To encode the idea of private helper functions, we define ourconceptual module’s functions in a letrec and then have the body of the letrec return the conceptualpublic functions in a list. So pos-rat-funs1 evaluates to a list of functions, all of which the outside world isallowed to use. The “private” functions gcd and reduce are not in the list and therefore unavailable. Sinceit is awkward and inconvenient to get functions out of a list before calling them, we define top-level variablesmake-frac, add, and print-rat that are bound to the appropriate functions.The reason this does not achieve modularity goals (1) and (3) is that clients can use the functions providedby (define-struct rat1 (num den)). So they can simply call make-rat1 directly to make rationals withnegative numbers. Or they can use rat1-num and rat1-den to see if the result of a call to add1 is in reducedform. Or they can use set-rat1-num! and set-rat1-den! to mutate the contents of a rational.To try to protect our “library” from unexpected errors given clients’ abilities, each “public” functionneeds to recheck the arguments, which we do with a call to the “private” function check-rat. This isannoying, potentially expensive, and in other examples it might not even be possible. If you are worriedabout concurrency, checking is even more difficult because a client could use set-rat1-num! after we checksomething, so it would be neces sary to copy a rational before doing the check.In pure Scheme (i.e., without DrScheme additions), we can’t really do better. No matter how we representrationals, clients will know the representation, so we cannot change the representation without potentiallychanging client behavior.1If we choose to use a cons cell, there is nothing to prevent clients from usingcons? to determine that and car and cdr to get the parts for example.However, with define-struct we define a new kind of thing. The only way to access the parts ofsomething made from make-rat1 is with rat1-num and rat1-den, so if we could hide those functions, wecan hide the representation of rationals. We could also hide rat1? so that we could even change the nameof our struct later, or we could choose to expose it so that clients can tell that they have a rat1 even thoughcannot access its parts. The key to all this is to put the (define-struct rat1 (num den)) itself in somescope and only expose some of the functions it defines to clients. In our example, this is a simple changethat then lets us significantly simplify the check-rat function and enforce all the invariants we want:(define pos-rat-funs2(let ()(define-struct rat2 (num den)) ; not globally visible now!(letrec([gcd ... unchanged ... ][reduce ... unchanged ... ]1We probably can avoid mutation problems with strange approaches using first-class functions, but this is hardly the naturalway to do a simple thing like define a strong interface.2[make-frac ... unchanged ... ][add ... unchanged ... ][print-rat ... unchanged ... ][check-rat (lambda (r) (if (not (rat2? r))(error "non-rational provided")))])(list make-frac add print-rat rat2?))))(define make-frac (car pos-rat-funs2))(define add (cadr pos-rat-funs2))(define print-rat (caddr pos-rat-funs2))(define rat? (cadddr pos-rat-funs2))As discussed above, this version chooses to expose the predicate for rationals, but not the constructor, theaccessors, or the mutators.Again, the essence of getting abstraction in a dynamically typed language is to make a new type of thinglike rat2 that clients have no way to “get at.” This seems quite different than our ML approach where wecould use an abstract type and rely on static type-checking to hide


View Full Document

UW CSE 341 - Lecture 19 Summary

Documents in this Course
Macros

Macros

6 pages

Macros

Macros

6 pages

Macros

Macros

3 pages

Mutation

Mutation

10 pages

Macros

Macros

17 pages

Racket

Racket

25 pages

Scheme

Scheme

9 pages

Macros

Macros

6 pages

Load more
Download Lecture 19 Summary
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Lecture 19 Summary and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Lecture 19 Summary 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?