Fear of Macros

Contents:
1 Preface
I learned Racket after 25 years of mostly using C and C++.
Some psychic whiplash resulted.
"All the parentheses" was actually not a big deal. Instead, the first +
Fear of Macros

Contents:
1 Preface
I learned Racket after 25 years of mostly using C and C++.
Some psychic whiplash resulted.
"All the parentheses" was actually not a big deal. Instead, the first mind warp was functional programming. Before long I wrapped my brain around it, and went on to become comfortable and effective with many other aspects and features of Racket.
But two final frontiers remained: Macros and continuations.
I found that simple macros were easy and understandable, plus there @@ -84,7 +84,7 @@ given:
> (syntax->datum #'(reverse-me "backwards" "am" "i" values)) '(reverse-me "backwards" "am" "i" values)
Using cdr slices off the first item of the list, reverse-me, leaving the remainder: ("backwards" "am" "i" values). Passing that to -reverse changes it to (values "i" "am" "backwards"):
> (reverse (cdr '("backwards" "am" "i" values))) '(values "i" "am")
Finally we use syntax->datum to convert this back to +reverse changes it to (values "i" "am" "backwards"):
> (reverse (cdr '(reverse-me "backwards" "am" "i" values))) '(values "i" "am" "backwards")
Finally we use datum->syntax to convert this back to syntax:
> (datum->syntax #f '(values "i" "am" "backwards")) #<syntax (values "i" "am" "backwards")>
That’s what our transformer function gives back to the Racket compiler, and that syntax is evaluated:
> (values "i" "am" "backwards") "i"
"am"
"backwards"
3.4 Compile time vs. run time
(define-syntax (foo stx) (make-pipe) ;Ce n'est pas le temps d'exécution #'(void)) Normal Racket code runs at ... run time. Duh.
Instead of "compile time vs. run time", you may hear it described as "syntax phase vs. runtime phase". Same difference.
But a syntax transformer is called by Racket as part of the process of @@ -223,8 +223,8 @@ mysterious.
4.1.2 require the module using for-syntax, since we need it at compile time:
> (require (for-syntax racket/syntax))
> (define-syntax (hyphen-define/ok3 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (with-syntax ([name (format-id stx "~a-~a" #'a #'b)]) #'(define (name args ...) body0 body ...))])) > (hyphen-define/ok3 bar baz () #t) > (bar-baz) #t
Using format-id is convenient as it handles the tedium of converting from syntax to symbol datum to string ... and all the way -back.
Finally, here’s a variation that accepts any number of name parts that -are joined with hyphens:
> (require (for-syntax racket/string racket/syntax))
> (define-syntax (hyphen-define* stx) (syntax-case stx () [(_ (names ...) (args ...) body0 body ...) (let* ([names/sym (map syntax-e (syntax->list #'(names ...)))] [names/str (map symbol->string names/sym)] [name/str (string-join names/str "-")] [name/sym (string->symbol name/str)]) (with-syntax ([name (datum->syntax stx name/sym)]) #`(define (name args ...) body0 body ...)))])) > (hyphen-define* (foo bar baz) (v) (* 2 v)) > (foo-bar-baz 50) 100
To review:
You can’t use a pattern variable outside of a template. But +back.
4.1.3 Another example
Finally, here’s a variation that accepts an arbitary number of name +parts to be joined with hyphens:
> (require (for-syntax racket/string racket/syntax))
> (define-syntax (hyphen-define* stx) (syntax-case stx () [(_ (names ...) (args ...) body0 body ...) (let* ([names/sym (map syntax-e (syntax->list #'(names ...)))] [names/str (map symbol->string names/sym)] [name/str (string-join names/str "-")] [name/sym (string->symbol name/str)]) (with-syntax ([name (datum->syntax stx name/sym)]) #`(define (name args ...) body0 body ...)))])) > (hyphen-define* (foo bar baz) (v) (* 2 v)) > (foo-bar-baz 50) 100
To review:
You can’t use a pattern variable outside of a template. But you can use syntax or #’ on a pattern variable to make an ad hoc, "fun size" template.
If you want to munge pattern variables for use in the template, with-syntax is your friend, because it lets you @@ -343,7 +343,8 @@ early draft and encouraged me to keep going. Sam Tobin-Hochstadt and Robby Findler also encouraged me. Matthew Flatt showed me how to make a Scribble interaction print syntax as "syntax" rather than as "#'". Jay McCarthy helped me -catch some mistakes and confusions.
Finally, I noticed something strange. After writing much of this, when +catch some mistakes and confusions. Jon Rafkind pointed out some +problems. Kieron Hardy reported a font issue and some typos.
Finally, I noticed something strange. After writing much of this, when I returned to some parts of the Racket documentation, I noticed it had improved since I last read it. Of course, it was the same. I’d changed. It’s interesting how much of what we already know is diff --git a/main.rkt b/main.rkt index a9bf5c3..927ae48 100644 --- a/main.rkt +++ b/main.rkt @@ -339,10 +339,10 @@ Using @racket[cdr] slices off the first item of the list, @racket[reverse] changes it to @racket[(values "i" "am" "backwards")]: @i[ -(reverse (cdr '("backwards" "am" "i" values))) +(reverse (cdr '(reverse-me "backwards" "am" "i" values))) ] -Finally we use @racket[syntax->datum] to convert this back to +Finally we use @racket[datum->syntax] to convert this back to @racket[syntax]: @i[ @@ -1547,7 +1547,8 @@ early draft and encouraged me to keep going. Sam Tobin-Hochstadt and Robby Findler also encouraged me. Matthew Flatt showed me how to make a Scribble @racket[interaction] print @racket[syntax] as @racket["syntax"] rather than as @racket["#'"]. Jay McCarthy helped me -catch some mistakes and confusions. +catch some mistakes and confusions. Jon Rafkind pointed out some +problems. Kieron Hardy reported a font issue and some typos. Finally, I noticed something strange. After writing much of this, when I returned to some parts of the Racket documentation, I noticed it had