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 +
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 @@ -212,15 +212,15 @@ Stepper. It says now we’re expanding to:
foo and bar. Which we get using syntax->datum:
> (define-syntax (hyphen-define/ok1 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (syntax-case (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b)))) () [name #'(define (name args ...) body0 body ...)])])) > (hyphen-define/ok1 foo bar () #t) > (foo-bar) #t
And now it works!
Next, some shortcuts.
4.1.1 with-syntax
Instead of an additional, nested syntax-case, we could use with-syntaxAnother name for -with-syntax could be, "define pattern variable".. This +with-syntax could be, "with new pattern variable".. This rearranges the syntax-case to look more like a let statement—
first the name, then the value. Also it’s more convenient -if we need to define more than one pattern variable.
> (define-syntax (hyphen-define/ok2 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (with-syntax ([name (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b))))]) #'(define (name args ...) body0 body ...))])) > (hyphen-define/ok2 foo bar () #t) > (foo-bar) #t
Whether you use an additional syntax-case or use -with-syntax, either way you are simply defining an additional -pattern variable. Don’t let the terminology and structure make it seem -mysterious.
4.1.2 with-syntax*
We may recall that let doesn’t let us use a definition in a -subsequent clause:
> (let ([a 0] [b a]) (values a b)) a: undefined;
cannot reference an identifier before its definition
in module: 'program
We could nest lets:
> (let ([a 0]) (let ([b a]) (values a b))) 0
0
Or we could use let*:
> (let* ([a 0] [b 0]) (values a b)) 0
0
Similarly there is a with-syntax* variation of -with-syntax:
> (require (for-syntax racket/syntax))
> (define-syntax (foo stx) (syntax-case stx () [(_ a) (with-syntax* ([b #'a] [c #'b]) #'c)])) One gotcha is that with-syntax* isn’t provided by +if we need to define more than one pattern variable.
> (define-syntax (hyphen-define/ok2 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (with-syntax ([name (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b))))]) #'(define (name args ...) body0 body ...))])) > (hyphen-define/ok2 foo bar () #t) > (foo-bar) #t
Again, with-syntax is simply syntax-case rearranged:
(syntax-case <syntax> () [<pattern> <body>]) (with-syntax ([<pattern> <syntax>]) <body>) Whether you use an additional syntax-case or use +with-syntax, either way you are simply defining additional +pattern variables. Don’t let the terminology and structure make it +seem mysterious.
4.1.2 with-syntax*
We know that let doesn’t let us use a binding in a subsequent +one:
> (let ([a 0] [b a]) b) a: undefined;
cannot reference an identifier before its definition
in module: 'program
Instead we can nest lets:
> (let ([a 0]) (let ([b a]) b)) 0
Or use a shorthand for nesting, let*:
> (let* ([a 0] [b a]) b) 0
Similarly, instead of writing nested with-syntaxs, we can use +with-syntax*:
> (require (for-syntax racket/syntax))
> (define-syntax (foo stx) (syntax-case stx () [(_ a) (with-syntax* ([b #'a] [c #'b]) #'c)])) One gotcha is that with-syntax* isn’t provided by racket/base. We must (require (for-syntax racket/syntax)). Otherwise we may get a rather bewildering error message:
...: ellipses not allowed as an expression in: ....
4.1.3 format-id
There is a utility function in racket/syntax called format-id that lets us format identifier names more diff --git a/main.rkt b/main.rkt index fc837b8..e4b97f3 100644 --- a/main.rkt +++ b/main.rkt @@ -845,7 +845,7 @@ Next, some shortcuts. Instead of an additional, nested @racket[syntax-case], we could use @racket[with-syntax]@margin-note*{Another name for -@racket[with-syntax] could be, "define pattern variable".}. This +@racket[with-syntax] could be, "with new pattern variable".}. This rearranges the @racket[syntax-case] to look more like a @racket[let] statement---first the name, then the value. Also it's more convenient if we need to define more than one pattern variable. @@ -864,40 +864,47 @@ if we need to define more than one pattern variable. (foo-bar) ] +Again, @racket[with-syntax] is simply @racket[syntax-case] rearranged: + +@racketblock[ +(syntax-case #,(italic "
") () [#,(bold " ") ]) +(with-syntax ([#,(bold " ") #,(italic " ")]) ) +] + Whether you use an additional @racket[syntax-case] or use -@racket[with-syntax], either way you are simply defining an additional -pattern variable. Don't let the terminology and structure make it seem -mysterious. +@racket[with-syntax], either way you are simply defining additional +pattern variables. Don't let the terminology and structure make it +seem mysterious. @subsubsection{@racket[with-syntax*]} -We may recall that @racket[let] doesn't let us use a definition in a -subsequent clause: +We know that @racket[let] doesn't let us use a binding in a subsequent +one: @i[ (let ([a 0] [b a]) - (values a b)) + b) ] -We could nest @racket[let]s: +Instead we can nest @racket[let]s: @i[ (let ([a 0]) (let ([b a]) - (values a b))) + b)) ] -Or we could use @racket[let*]: +Or use a shorthand for nesting, @racket[let*]: @i[ (let* ([a 0] - [b 0]) - (values a b)) + [b a]) + b) ] -Similarly there is a @racket[with-syntax*] variation of -@racket[with-syntax]: +Similarly, instead of writing nested @racket[with-syntax]s, we can use +@racket[with-syntax*]: @i[ (require (for-syntax racket/syntax))