CSE341: Programming Languages Lecture 16 Macros Dan Grossman Fall 2011This lecture • What are macros • Why are macros difficult to use sensibly • Using Racket’s macro system – Defining macros – Watching out for evaluation order and (re)-evaluation – Why hygiene makes Racket’s macros much easier to use sensibly • When (not) to use macros Fall 2011 2 CSE341: Programming LanguagesWhat is a macro • A macro describes how to transform some new syntax into different syntax in the source language • A macro is one way to implement syntactic sugar – “Replace any syntax of the form e1 andalso e2 with if e1 then e2 else false” • A macro system is a language (or part of a larger language) for defining macros • Macro expansion is the process of rewriting the syntax to eliminate macro uses – Before a program is run (or even compiled) Fall 2011 3 CSE341: Programming LanguagesTokenization First question for a macro system: How does it tokenize? • Macro systems generally work at the level of tokens not sequences of characters – So must know how programming language tokenizes text • Example: “replace all occurrences of car with hd” – Would not rewrite (+ cart foo) to (+ hdt foo) – Would not rewrite car-door to hd-door • But would in C where car-door is subtraction Fall 2011 4 CSE341: Programming LanguagesParenthesization Second question for a macro system: How does associativity work? C/C++ basic example: Probably not what you wanted: means not So C macro writers use lots of parentheses, which is fine: Racket won’t have this problem: – Macro use: (macro-name …) – After expansion: ( something else in same parens ) Fall 2011 5 CSE341: Programming Languages #define ADD(x,y) x+y ADD(1,2/3)*4 1 + 2 / 3 * 4 (1 + 2 / 3) * 4 #define ADD(x,y) ((x)+(y))Local bindings Third question for a macro system: Can variables shadow macros? Suppose macros also apply to variable bindings. Then: Would become: This is why C/C++ convention is all-caps macros and non-all-caps for everything else Racket gets this and other scope gotchas “right” Fall 2011 6 CSE341: Programming Languages (let ([hd 0][car 1]) hd) ; 0 (let* ([hd 0][car 1]) hd) ; 0 (let ([car 0][car 1]) car) ; error (let* ([car 0][car 1]) car) ; 1Example Racket macro definitions Two simple macros Fall 2011 7 CSE341: Programming Languages (define-syntax my-if ; macro name (syntax-rules (then else) ; other keywords [(my-if e1 then e2 else e3) ; macro use (if e1 e2 e3)])) ; form of expansion (define-syntax comment-out ; macro name (syntax-rules () ; other keywords [(comment-out ignore instead) ; macro use instead])) ; form of expansion If the form of the use matches, do the corresponding expansion – In these examples, list of possible use forms has length 1 – Else syntax errorExample uses Fall 2011 8 CSE341: Programming Languages (my-if x then y else z) ; (if x y z) (my-if x then y then z) ; syntax error (my-if x then (begin (print "hi") 34) then 15) (comment-out (begin (print "hi") 34) 15) (comment-out (car null) #f) It’s like we added keywords to our language – Other keywords only keywords in uses of that macro – Syntax error if keywords misused – Rewriting (“expansion”) happens before executionRevisiting delay and force Recall our definition of promises from last lecture – Should we use a macro instead to avoid clients’ explicit thunk? Fall 2011 9 CSE341: Programming Languages (define (my-delay th) (mcons #f th)) (define (my-force p) (if (mcar p) (mcdr p) (begin (set-mcar! p #t) (set-mcdr! p ((mcdr p))) (mcdr p)))) (define (f p) (… (my-force p) …)) (f (my-delay (lambda () e)))A delay macro • A macro can put an expression under a thunk – Delays evaluation without explicit thunk – Cannot implement this with a function • Now client then should not use a thunk (that would double-thunk) – Racket’s pre-defined delay is a similar macro Fall 2011 10 CSE341: Programming Languages (define-syntax my-delay (syntax-rules () [(my-delay e) (mcons #f (lambda() e))])) (f (my-delay e))What about a force macro? We could define my-force with a macro too – Good macro style would be to evaluate the argument exactly once (use x below, not multiple evaluations of e) – Which shows it is bad style to use a macro at all here! – Don’t use macros when functions do what you want Fall 2011 11 CSE341: Programming Languages (define-syntax my-force (syntax-rules () [(my-force e) (let([x e]) (if (mcar x) (mcdr x) (begin (set-mcar! x #t) (set-mcdr! p ((mcdr p))) (mcdr p))))]))Another bad macro Any function that doubles its argument is fine for clients – These are equivalent to each other So macros for doubling are bad style but instructive examples: – These are not equivalent to each other. Consider: Fall 2011 12 CSE341: Programming Languages (define (dbl x) (+ x x)) (define (dbl x) (* 2 x)) (define-syntax dbl (syntax-rules()[(dbl x)(+ x x)])) (define-syntax dbl (syntax-rules()[(dbl x)(* 2 x)])) (dbl (begin (print "hi") 42))More examples Sometimes a macro should re-evaluate an argument it is passed – If not, as in dbl, then use a local binding as needed: Also good style for macros not to have surprising evaluation order – Good rule of thumb to preserve left-to-right – Bad example (fix with a local binding): Fall 2011 13 CSE341: Programming Languages (define-syntax dbl (syntax-rules () [(dbl x) (let ([y x]) (+ y y))])) (define-syntax take (syntax-rules (from) [(take e1 from e2) (- e2 e1)]))Local variables in macros In C/C++, defining local variables inside macros is unwise – When needed done with hacks like __strange_name34 Here’s why with a silly example: – Macro: – Use: – Naïve expansion: – But instead Racket “gets it right,” which is part of hygiene Fall 2011 14 CSE341: Programming Languages (define-syntax dbl (syntax-rules () [(dbl x) (let ([one 1]) (* 2 x one))])) (let ([one 7]) (dbl one)) (let ([one 7]) (let* ([one 1]) (* 2 one one)))The other side of hygiene This also looks like it would do the “wrong” thing – But Racket’s hygienic macros do the “right thing” – Macro: – Use: – Naïve expansion: Fall 2011 15 CSE341: Programming Languages (define-syntax dbl
View Full Document