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 @@ -115,15 +115,13 @@ syntax list. We didn’t take (name when we used pattern-matching. In general, a syntax transformer won’t care about that, because it is the name of the transformer binding. In other words, a macro usually doesn’t care -about its own name.
Instead of:
> (define-syntax (our-if-v2 stx) (define xs (syntax->list stx)) (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)] [else ,(cadddr xs)])))
We can write:
> (define-syntax (our-if-using-match stx) (match (syntax->list stx) [(list name condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] [else ,false-expr]))])) > (our-if-using-match #t "true" "false") match: undefined;
cannot reference an identifier before its definition
in module: 'program
phase: 1
But wait, we can’t. It’s complaining that match isn’t -defined. We haven’t required the racket/match module?
It turns out we haven’t. Remember, this transformer function is
-working at compile time, not run time. And at compile time, only
-racket/base is required for you automatically. If we want
-something like racket/match, we have to require it
-ourselves—
So let’s try that:
> (require (for-syntax racket/match))
> (define-syntax (our-if-using-match-v2 stx) (match (syntax->list stx) [(list _ condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] [else ,false-expr]))])) > (our-if-using-match-v2 #t "true" "false") "true"
Joy.
3.5 begin-for-syntax
We used for-syntax to require the +about its own name.
Instead of:
> (define-syntax (our-if-v2 stx) (define xs (syntax->list stx)) (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)] [else ,(cadddr xs)])))
We can write:
> (define-syntax (our-if-using-match stx) (match (syntax->list stx) [(list name condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] [else ,false-expr]))]))
Great. Now let’s try using it:
> (our-if-using-match #t "true" "false") match: undefined;
cannot reference an identifier before its definition
in module: 'program
phase: 1
Oops. It’s complaining that match isn’t defined.
Our transformer function is working at compile time, not run time. And
+at compile time, only racket/base is required for you
+automatically—
Anything beyond racket/base, we have to require
+ourselves—
In this case, instead of using plain (require racket/match),
+we want (require (for-syntax racket/match))—
So let’s try that:
> (require (for-syntax racket/match))
> (define-syntax (our-if-using-match-v2 stx) (match (syntax->list stx) [(list _ condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] [else ,false-expr]))])) > (our-if-using-match-v2 #t "true" "false") "true"
Joy.
3.5 begin-for-syntax
We used for-syntax to require the racket/match module because we needed to use match at compile time.
What if we wanted to define our own helper function to be used by a macro? One way to do that is put it in another module, and diff --git a/main.rkt b/main.rkt index 9f0d473..1163a15 100644 --- a/main.rkt +++ b/main.rkt @@ -496,21 +496,27 @@ We can write: (match (syntax->list stx) [(list name condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] - [else ,false-expr]))])) + [else ,false-expr]))]))] + +Great. Now let's try using it: + +@i[ (our-if-using-match #t "true" "false") ] -But wait, we can't. It's complaining that @racket[match] isn't -defined. We haven't required the @racket[racket/match] module? +Oops. It's complaining that @racket[match] isn't defined. -It turns out we haven't. Remember, this transformer function is -working at compile time, not run time. And at compile time, only -@racket[racket/base] is required for you automatically. If we want -something like @racket[racket/match], we have to require it -ourselves---and require it @italic{for compile time}. Instead of using -plain @racket[(require racket/match)], the way to say this is to use -@racket[(require (for-syntax racket/match))]---the @racket[for-syntax] -part meaning, "for compile time". +Our transformer function is working at compile time, not run time. And +at compile time, only @racket[racket/base] is required for you +automatically---not the full @racket[racket]. + +Anything beyond @racket[racket/base], we have to require +ourselves---and require it for compile time using the +@racket[for-syntax] form of @racket[require]. + +In this case, instead of using plain @racket[(require racket/match)], +we want @racket[(require (for-syntax racket/match))]---the +@racket[for-syntax] part meaning, "for compile time". So let's try that: