DOC PREVIEW
UW CSE 341 - Lecture Notes

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:

CSE341, Fall 2011, Lecture 24 SummaryStandard Disclaimer: This lecture summary is not necessarily a complete substitute for attending class,reading the associated code, etc. It is designed to be a useful resource for students who attended class andare later reviewing the material.This lecture discusses some basic features of Racket’s module system. It is a good complement to our earlierlecture on SML’s module system and, for this purpose, we use the same example as in that lecture: a smalllibrary for rational numbers. This is a less inspired choice in Racket because the Racket language alreadyhas rationals as a built-in primitive type, but the example can still serve our purposes.A common misconception is that one needs static typing to support abstract types in a language. We showthat Racket’s structs are sufficient because they create a new dynamic type, distinct from all existing types,and we can hide the constructor for this type from client code. In fact, we show that we can do this evenwithout modules (just using local scope and closures) but that modules are better style, easier to use, andsupport many useful features like renaming bindings that are exported or imported. Finally we give a briefdemonstration of Racket’s contract system, which can check arbitrary properties of arguments to and resultsfrom cross-module function calls at run-time.Recall ML’s Modules and Our Rational-Number ExampleWe previously studied in ML that a module system can provide namespace management (avoiding nameconflicts for bindings in large applications), hide private bindings (allowing the module implementer tochange/remove implementation details later), and enforce invariants by using abstract types (making datarepresentation inaccessible to clients). In ML, we used a signature like this one to describe the interface toa rational-number module:sigtype rationalexception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval print_rat : rational -> unitendThe implementation of our module ensured that rationals were always printed in reduced form (e.g., 2/3instead of 6/9). Different implementations can do so in different ways by maintaining different internalinvariants. For example, we can keep all values of type rational in reduced form and keep all denominatorspositive.This approach used two techniques. First, by not including bindings for private functions like gcd (forcomputing the greatest common divisor) and reduce (for reducing a rational), the module does not have tospecify their behavior. Second, by making rational an abstract type, we prevent clients from creating valuesof type rational on their own or inspecting their representation. This allows us to change the representationor our internal invariants without changing the behavior of clients.Using Racket’s struct Definitions for Abstract Types Without ModulesBefore showing Racket’s module system, we show that one does not need modules to hide private bindings— local scope and first-class functions are enough. This is not specific to Racket; we could have done thesame thing in ML. It is not great style — this is what modules are for — but it helps teach the concepts ofmodularity and information hiding.Consider this close-but-not-quite-right approach, where we omit here the full definition of the functionsdefined in the letrec binding (see the code accompanying this summary for the full definitions):1(struct (rat num den)(define rat-funs(letrec([gcd (lambda (x y) ...)][reduce (lambda (x y) ...)][make-frac (lambda (x y) ...)][add (lambda (r1 r2) ...)][print-rat (lambda (r) ...)])(list make-frac add print-rat)))(define make-frac (car rat-funs))(define add (cadr rat-funs))(define print-rat (caddr rat-funs))The key binding is rat-funs, which evaluates a letrec to produce a list of functions. The five functionsbound to local variables gcd, reduce, make-frac, add, and print-rat can all call each other (the purposeof letrec) and the variables are in scope in the body of the letrec. The body just creates a list of threeclosures. So after the letrec evaluates, only these three closures are directly accessible via rat-funs; theother functions are “private” in the sense that they can only be called indirectly via the three functionsin the list, which is what we want for modularity. Since accessing these functions via list-processing is notsomething clients would want to do, we bind them to top-level variables make-frac, add, and print-rat inthe last three lines. The choice of variables names in the letrec and in these lines need not have anythingto do with each other.Unfortunately, the code above does not achieve the modularity we want. The reason is that the definition(struct (rat num den)) is at top-level, so it is available to clients. So clients can make rationals violatingour invariants directly, such as (rat 9 6), (rat 2 -3), or (rat "hi" 0). The solution is to put the structdefinition itself in a local scope so that its functions (or at least the ones we don’t want to make available)are not in scope for the clients. We can this as follows, by using Racket’s internal defines instead of letrec(the meaning is the same, but letrec doesn’t allow struct definitions):(define rat-funs(let ()(struct (rat num den)(define (gcd x y) ...)(define (reduce x y) ...)(define (make-frac x y) ...)(define (add r1 r2) ...)(define (print-rat r) ...)(list make-frac add print-rat)))(define make-frac (car rat-funs))(define add (cadr rat-funs))(define print-rat (caddr rat-funs))Actually, it would be nice to let clients use the rat? predicate in their own code since in a dynamically typedlanguage it can be useful to test at run-time, “is this a rational?” We can easily do so by adding rat? to thelist of functions returned since it is in scope inside the definition of rat-funs just like all the other functionsthat the struct definition introduces.The fundamental reason this technique works is that a struct definition creates a new dynamic type. The2only way to make a rational is to use the rat constructor. The rat? predicate answers #t for values created bythis constructor and every other predicate, such as cons?, answers #f for values created by this constructor.If Racket did not have a way to make new dynamic types, then we could not enforce invariants and modularity.For example, if struct was some sort of syntactic sugar for building data structures with cons cells, thenclients could use cons? to


View Full Document

UW CSE 341 - Lecture Notes

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 Notes
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 Notes 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 Notes 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?