; CSE 341, Fall 2011, Lecture 16 ; Macros #lang racket ;; a cosmetic macro -- adds then, else (define-syntax my-if (syntax-rules (then else) [(my-if e1 then e2 else e3) (if e1 e2 e3)])) ;; a silly (?) macro to replace an expression with another one (define-syntax comment-out (syntax-rules () [(comment-out ignore instead) instead])) ;; a macro to delay evaluation (call-by-need) ; makes it so users don't write the thunk (define-syntax my-delay (syntax-rules () [(my-delay e) (mcons #f (lambda () e))])) ; poor use of a macro: a function would work just as well (define-syntax my-force-m (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))))])) (define (my-force p) (if (mcar p) (mcdr p) (begin (set-mcar! p #t) (set-mcdr! p ((mcdr p))) (mcdr p)))) ;; another bad-style macro -- a function is better (define-syntax double (syntax-rules () [(double e) (* 2 e)])) ;(define (double x) (* 2 x)) ;; a worse macro -- effects repeated (define-syntax double2 (syntax-rules () [(double2 e) (+ e e)])) ;; equal in badness to first double macro, thanks to hygiene (define-syntax double3 (syntax-rules () [(double3 e) (let* ([zero 0] [x e]) (+ x x zero))])) ;; in particular, hygiene makes this work how we want (define (f zero) (double3 zero)) (define x (f 17)) ;; another example where you want to use locals (define-syntax bad-from (syntax-rules () [(bad-from e1 e2) (- e2 e1)])) ; evaluates right-to-left ;; and the other side of hygiene (define (fourtimes x) (let ([* +]) ;; terrible style but at least it does not break double (double (double x)))) ;; some better examples ;; let2 allows up to two local bindings (with let* semantics) with fewer parentheses ;; than let* (define-syntax let2 (syntax-rules () [(let2 () body) body] [(let2 (var val) body) (let ([var val]) body)] [(let2 (var1 val1 var2 val2) body) (let ([var1 val1]) (let ([var2 val2]) body))])) ;; a loop that executes body hi - lo times ;; notice use of local variables (define-syntax for (syntax-rules (to do) [(for lo to hi do body) (let ([l lo] [h hi]) (letrec ([loop (lambda (it) (if (> it h) #t (begin body (loop (+ it 1)))))]) (loop l)))])) ;; the special ... lets us take any number of arguments ;; Note: nothing prevents infinite code generation! (define-syntax my-let* (syntax-rules () [(my-let* () body) body] [(my-let* ([var0 val0] [var-rest val-rest] ...) body) (let ([var0 val0]) (my-let* ([var-rest val-rest] ...) body))]))