DOC PREVIEW
UW CSE 341 - Lecture Notes

This preview shows page 1 out of 4 pages.

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

Unformatted text preview:

CSE 341, Spring 2011, Lecture 15 SummaryLast update: 5/1/11 to use Racket’s mutable cons cells in delay/force.Standard 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 discusses macros, which let programmers extend the syntax of a programming language. Amacro definition describes how a macro use is rewritten into other syntax that is (already) in the programminglanguage. There are many inappropriate uses of macros and many languages with bad macro systems.Fortunately, Scheme has a extraordinarily good and powerful approach to macros and there are some goodidioms using macros. So we will study Scheme’s macro system and through it learn some of the pitfalls ofmacros in general. More specifically, we will see how Scheme’s macro system is hygienic (a technical termdescribed below), which is usually what you want and helps avoid many (but not all) of macros’ problems.The first thing we can consider with macros is how the rewriting is defined. For example, consider amacro that, “replaces every use of car with hd.” In macros systems that does not mean some variable cartwould be rewritten as hdt. So the implementation of macros has to at least understand how a programminglanguage’s text is broken into tokens (i.e., words). We can then ask if macros do or do not understandparenthesization. For example, in C/C++, if you have a macro#define ADD(x,y) x+ythen ADD(1,2/3)*4 gets rewritten as 1 + 2 / 3 * 4, which is not the same thing as (1 + 2/3)*4. So insuch languages, macro writers generally include lots of explicit parentheses in their macro definitions, e.g.,#define ADD(x,y) ((x)+(y))In Scheme, macro expansion preserves the code structure so this issue is not a problem. Finally, we can askif macro expansion happens for binding occurrences. If not, then local variables can shadow macros, whichis probably what you want. For example, suppose we have:(let* ([hd 0] [car 1]) hd) ; evaluates to 0If we replace the car above with hd, then the expression now evaluates to 1. In Scheme, macro expansiondoes not apply to variable definitions, i.e., the car above is different and shadows any macro for car thathappens to be in scope.Let’s now walk through the syntax we will use to define macros in Scheme. (There have been manyvariations just in Scheme over the years; this is a modern approach we will use in this course.) Here is amacro that lets users write (my-if e1 then e2 else e3) for any expressions e1, e2, and e3 and have itmean exactly (if e1 e2 e3):(define-syntax my-if(syntax-rules (then else)[(my-if e1 then e2 else e3)(if e1 e2 e3)]))• define-syntax is the special form for defining a macro.• my-if is the name of our macro. It adds to the environment so that expressions of the form (my-if ...)will be rewritten according to the syntax rules in the rest of the macro definition.• syntax-rules is a keyword• The next parenthesized list (in this case (then else)) is a list of “keywords” for this macro, i.e., anyuse of then or else is just syntax whereas anything not in this list (or my-if itself) represents anarbitrary expression.1• The rest is a list of pairs: how my-if might be used and how it should be rewritten if it used that way.• In this example, our list has only one option, my-if must be used in an expression of the form(my-if e1 then e2 else e3) and that becomes (if e1 e2 e3). Otherwise an error result. Notethe rewriting occurs before any evaluation of the expressions e1, e2, or e3, unlike with functions. Thisis what we want for a conditional expression like my-if.Whereas my-if creates a more verbose way of writing something we already could do, these macrosprovide a more concise way to use “delay” and “force” as described in the previous lecture:(define-syntax my-delay(syntax-rules ()[(my-delay e)(mcons #f (lambda () e))]))(define-syntax my-force(syntax-rules ()[(my-force e)(let ([x e])(if (mcar x)(mcdr x)(begin (set-mcar! x #t)(set-mcdr! x ((mcdr x)))(mcdr x))))]))We can write (my-delay some-computation) to get back something and some-computation will not beexecuted until that something is “passed” to my-force. The convenience here is that the user of my-delaydoes not write the thunk, that is inserted by the rewriting in the definition of the my-delay macro.In the definition of my-force it is very good style that we use a local variable (x) to hold the result ofevaluating e. Otherwise, we would evaluate e multiple times. In code like:(let ([t (my-delay some-complicated-expression)])(my-force t))this does not matter since t is already bound to a value, but in code like:(my-force (my-delay some-complicated-expression))not using x would lead to creating multiple different thunks. Now code like (my-force (my-delay ...))is not very common, but that is no reason for my-force not to work as expected even for this case.As a simpler but less useful example that lets us investigate the issue of when macro arguments areevaluated and in what environment, let’s consider a macro that doubles its argument. This is poor stylebecause if you want to double an argument you should just write a function: (define (double x) (* 2 x))or (define (double x) (+ x x)) which are both equivalent. That said, the “macro versions” are notequivalent:(define-syntax double1(syntax-rules ()[(double1 e)(* 2 e)]))(define-syntax double2(syntax-rules ()[(double2 e)(+ e e)]))2The reason is double2 will evaluate its argument twice. So (double1 (begin (print "hi") 17)) prints"hi" once but (double2 (begin (print "hi") 17)) prints "hi" twice. The function versions print "hi"once, simply because, as always, function arguments are evaluated to values before the function is called.To fix double2 without “changing the algorithm” to multiplication instead of addition, we use a localvariable like we did in my-force:(define-syntax double3(syntax-rules ()[(double3 e)(let ([x e])(+ x x))]))Using local variables in macro definitions to control if/when expressions get evaluated is exactly whatyou should do, but in less powerful macro languages (again, C/C++ is an easy target for derision here),local variables in macros are typically avoided. The reason has to do with scope and something that is calledhygiene. For sake of example, consider this silly variant of double3:(define-syntax


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?