├── .gitignore
├── gh.css
├── COPYRIGHT
├── .github
└── FUNDING.yml
├── fear-of-macros.jpg
├── macro-stepper.png
├── README.md
├── Makefile
├── add-to-head.rkt
└── index.rkt
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | html/
3 | fear-of-macros/
4 |
--------------------------------------------------------------------------------
/gh.css:
--------------------------------------------------------------------------------
1 | .strike {
2 | text-decoration: line-through;
3 | }
4 |
--------------------------------------------------------------------------------
/COPYRIGHT:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 by Greg Hendershott. All rights reserved.
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: greghendershott
2 | custom: https://www.paypal.me/greghendershott
3 |
--------------------------------------------------------------------------------
/fear-of-macros.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greghendershott/fear-of-macros/HEAD/fear-of-macros.jpg
--------------------------------------------------------------------------------
/macro-stepper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greghendershott/fear-of-macros/HEAD/macro-stepper.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## What it is
4 |
5 | This is a rough draft of a practical guide to Racket macros.
6 |
7 | I started to write it not because I understand macros very well, but
8 | because I don't.
9 |
10 | ## Where to read it
11 |
12 | - [Multiple HTML files](https://www.greghendershott.com/fear-of-macros/index.html)
13 | - [One big HTML file](https://www.greghendershott.com/fear-of-macros/all.html)
14 |
15 | ## Feedback
16 |
17 | Feedback is welcome; please use Issues page here on GitHub.
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | scrbl := index.rkt
2 |
3 | www := fear-of-macros
4 |
5 | .PHONY: all clean html html-single html-multi
6 |
7 | all: html
8 |
9 | clean:
10 | rm -rf $(www)
11 |
12 | html: html-single html-multi
13 | racket add-to-head.rkt
14 |
15 | html-single: $(scrbl)
16 | raco scribble \
17 | --html \
18 | --dest $(www) \
19 | --dest-name all.html \
20 | ++style gh.css \
21 | ++main-xref-in \
22 | --redirect-main https://docs.racket-lang.org/ \
23 | \
24 | $(scrbl)
25 |
26 | html-multi: $(scrbl)
27 | raco scribble \
28 | --htmls \
29 | --dest-name $(www) \
30 | ++style gh.css \
31 | ++main-xref-in \
32 | --redirect-main https://docs.racket-lang.org/ \
33 | \
34 | $(scrbl)
35 |
36 | ######################################################################
37 | # S3 bucket deploy
38 |
39 | aws := aws --profile greg
40 | dest := s3://www.greghendershott.com/fear-of-macros/
41 | cfid := E2LPR1YW069SHG
42 |
43 | .PHONY: deploy
44 |
45 | deploy:
46 | $(aws) s3 sync --no-follow-symlinks $(www) $(dest)
47 | $(aws) cloudfront create-invalidation --distribution-id $(cfid) --paths "/fear-of-macros/*"
48 |
--------------------------------------------------------------------------------
/add-to-head.rkt:
--------------------------------------------------------------------------------
1 | #lang racket
2 |
3 | ;; Realm of kludge:
4 | ;;
5 | ;; AFIK no way via Scribble to put something into the
section.
6 | ;;
7 | ;; This reads all HTML files and injects some stuff immediately before the
8 | ;; closing tag.
9 |
10 | (define (meta k v)
11 | (format "" k v))
12 |
13 | (define metas
14 | (string-append
15 | (meta "keywords" "Racket, macros, Scheme")
16 | (meta "description" "Practical Racket macros")
17 | (meta "author" "Greg Hendershott")
18 | (meta "charset" "utf-8")))
19 |
20 | (define "")
21 |
22 | (define all (string-append metas ))
23 | (define subst (regexp-replace* "\n" all "")) ;minify
24 |
25 | (define (do-file path)
26 | (define old (file->string path))
27 | (define new (regexp-replace old subst))
28 | (with-output-to-file path
29 | (lambda () (display new))
30 | #:mode 'text
31 | #:exists 'replace))
32 |
33 | (require racket/runtime-path)
34 | (define-runtime-path here ".")
35 | (for ([path (find-files (lambda (path)
36 | (regexp-match? #rx"\\.html" path))
37 | here)])
38 | (do-file path))
39 |
--------------------------------------------------------------------------------
/index.rkt:
--------------------------------------------------------------------------------
1 | #lang scribble/manual
2 |
3 | @(require racket/sandbox
4 | scribble/eval
5 | scribble/racket
6 | racket/date
7 | (for-syntax racket/base)
8 | (for-label racket)
9 | (for-label racket/stxparam)
10 | (for-label syntax/parse)
11 | (for-label racket/splicing)
12 | (for-label racket/syntax))
13 | @(define evaluator
14 | (parameterize ([sandbox-output 'string]
15 | [sandbox-error-output 'string]
16 | [sandbox-memory-limit #f])
17 | (make-evaluator 'racket)))
18 |
19 | @(define typed/evaluator
20 | (parameterize ([sandbox-output 'string]
21 | [sandbox-error-output 'string]
22 | [sandbox-memory-limit #f])
23 | (make-evaluator 'typed/racket)))
24 |
25 | @(define-syntax-rule (i body ...)
26 | (interaction #:eval evaluator body ...))
27 |
28 | @(define (current-year)
29 | (number->string (date-year (current-date))))
30 |
31 | @title[#:version ""]{Fear of Macros}
32 | @author[@hyperlink["http://www.greghendershott.com"
33 | "Greg Hendershott"]]
34 | @image["fear-of-macros.jpg"]
35 | @para{A practical guide to @hyperlink["https://www.racket-lang.org"]{Racket} macros.}
36 | @para[@smaller{Copyright (c) 2012-@current-year[] by Greg Hendershott. All rights reserved.}]
37 | @para[@smaller["Last updated "
38 | (parameterize ([date-display-format 'iso-8601])
39 | (date->string (current-date) #t))]]
40 | @para{@hyperlink["https://github.com/greghendershott/fear-of-macros/issues" "Feedback and corrections"].}
41 | @para{@hyperlink["https://github.com/users/greghendershott/sponsorship" "Sponsor my work"] or @hyperlink["https://www.paypal.me/greghendershott" "donate"].}
42 |
43 | @table-of-contents{}
44 |
45 | @; ----------------------------------------------------------------------------
46 |
47 | @section{Preface}
48 |
49 | I learned @hyperlink["https://www.racket-lang.org"]{Racket} after 25
50 | years of mostly using C and C++.
51 |
52 | Some psychic whiplash resulted.
53 |
54 | "All the parentheses" was actually not a big deal. Instead, the first
55 | mind warp was functional programming. Before long I wrapped my brain
56 | around it, and went on to become comfortable and effective with many
57 | other aspects and features of Racket.
58 |
59 | But two final frontiers remained: Macros and continuations.
60 |
61 | I found that simple macros were easy and understandable, plus there
62 | were many good tutorials available. But the moment I stepped past
63 | routine pattern-matching, I kind of fell off a cliff into a
64 | terminology soup. I marinaded myself in material, hoping it would
65 | eventually sink in after enough re-readings. I even found myself using
66 | trial and error, rather than having a clear mental model what was
67 | going on. Gah.
68 |
69 | I'm starting to write this at the point where the shapes are slowly
70 | emerging from the fog.
71 |
72 | @margin-note{If you have any corrections, criticisms, complaints, or whatever,
73 | @hyperlink["https://github.com/greghendershott/fear-of-macros/issues" "please
74 | let me know"].}
75 |
76 | My primary motive is selfish. Explaining something forces me to learn
77 | it more thoroughly. Plus if I write something with mistakes, other
78 | people will be eager to point them out and correct me. Is that a
79 | social-engineering variation of meta-programming? Next question,
80 | please. :)
81 |
82 | Finally I do hope it may help other people who have a similar
83 | background and/or learning style as me.
84 |
85 | I want to show how Racket macro features have evolved as solutions to
86 | problems or annoyances. I learn more quickly and deeply when I
87 | discover the answer to a question I already have, or find the solution
88 | to a problem whose pain I already feel. Therefore I'll give you the
89 | questions and problems first, so that you can better appreciate and
90 | understand the answers and solutions.
91 |
92 | @; ----------------------------------------------------------------------------
93 |
94 | @section{Our plan of attack}
95 |
96 | The macro system you will mostly want to use for production-quality
97 | macros is called @racket[syntax-parse]. And don't worry, we'll get to
98 | that soon.
99 |
100 | But if we start there, you're likely to feel overwhelmed by concepts
101 | and terminology, and get very confused. I did.
102 |
103 | 1. Instead let's start with the basics: A syntax object and a function
104 | to change it---a "transformer". We'll work at that level for a while to
105 | get comfortable and to de-mythologize this whole macro business.
106 |
107 | 2. Soon we'll realize that pattern-matching would make life
108 | easier. We'll learn about @racket[syntax-case] and its shorthand
109 | cousin, @racket[define-syntax-rule]. We'll discover we can get
110 | confused if we want to munge pattern variables before sticking them
111 | back in the template, and learn how to do that.
112 |
113 | 3. At this point we'll be able to write many useful macros. But, what
114 | if we want to write the ever-popular anaphoric if, with a "magic
115 | variable"? It turns out we've been protected from making certain kind
116 | of mistakes. When we want to do this kind of thing on purpose, we use
117 | a syntax parameter. [There are other, older ways to do this. We won't
118 | look at them. We also won't spend a lot of time
119 | advocating "hygiene"---we'll just stipulate that it's good.]
120 |
121 | 4. Finally, we'll realize that our macros could be smarter when
122 | they're used in error. Normal Racket functions optionally can have
123 | contracts and types. These catch usage mistakes and provide clear,
124 | useful error messages. It would be great if there were something
125 | similar for macro. There is. One of the more-recent Racket macro
126 | enhancements is @racket[syntax-parse].
127 |
128 |
129 | @; ----------------------------------------------------------------------------
130 | @; ----------------------------------------------------------------------------
131 |
132 | @section{Transform!}
133 |
134 | @verbatim[#:indent 2]{
135 | YOU ARE INSIDE A ROOM.
136 | THERE ARE KEYS ON THE GROUND.
137 | THERE IS A SHINY BRASS LAMP NEARBY.
138 |
139 | IF YOU GO THE WRONG WAY, YOU WILL BECOME
140 | HOPELESSLY LOST AND CONFUSED.
141 |
142 | > pick up the keys
143 |
144 | YOU HAVE A SYNTAX TRANSFORMER
145 | }
146 |
147 |
148 | @subsection{What is a syntax transformer?}
149 |
150 | A syntax transformer is not one of the トランスフォーマ
151 | @hyperlink["http://en.wikipedia.org/wiki/Transformers" "transformers"].
152 |
153 | Instead, it is simply a function. The function takes syntax and
154 | returns syntax. It transforms syntax.
155 |
156 | Here's a transformer function that ignores its input syntax, and
157 | always outputs syntax for a string literal:
158 |
159 | @margin-note{These examples assume @litchar{#lang racket}. If you want
160 | to try them using @litchar{#lang racket/base}, you'll need to
161 | @litchar{(require (for-syntax racket/base))}.}
162 |
163 | @(let-syntax([syntax (make-element-id-transformer
164 | (lambda (stx)
165 | #'@racket[syntax]))]) ;print as syntax not #'
166 | @i[
167 | (define-syntax foo
168 | (lambda (stx)
169 | (syntax "I am foo")))
170 | ]
171 | )
172 |
173 | Using it:
174 |
175 | @i[
176 | (foo)
177 | ]
178 |
179 | When we use @racket[define-syntax], we're making a transformer
180 | @italic{binding}. This tells the Racket compiler, "Whenever you
181 | encounter a chunk of syntax starting with @racket[foo], please give it
182 | to my transformer function, and replace it with the syntax I give back
183 | to you." So Racket will give anything that looks like @racket[(foo
184 | ...)] to our function, and we can return new syntax to use
185 | instead. Much like a search-and-replace.
186 |
187 | Maybe you know that the usual way to define a function in Racket:
188 |
189 | @racketblock[(define (f x) ...)]
190 |
191 | is shorthand for:
192 |
193 | @racketblock[(define f (lambda (x) ...))]
194 |
195 | That shorthand lets you avoid typing @racket[lambda] and some parentheses.
196 |
197 | Well there is a similar shorthand for @racket[define-syntax]:
198 |
199 | @(let-syntax([syntax (make-element-id-transformer
200 | (lambda (stx)
201 | #'@racket[syntax]))]) ;print as syntax not #'
202 | @i[
203 | (define-syntax (also-foo stx)
204 | (syntax "I am also foo"))
205 | (also-foo)
206 | ]
207 | )
208 |
209 | What we want to remember is that this is simply shorthand. We are
210 | still defining a transformer function, which takes syntax and returns
211 | syntax. Everything we do with macros, will be built on top of this
212 | basic idea. It's not magic.
213 |
214 | Speaking of shorthand, there is also a shorthand for @racket[syntax],
215 | which is @litchar{#'}:
216 |
217 | @margin-note{@litchar{#'} is short for @racket[syntax] much like
218 | @litchar{'} is short for @racket[quote].}
219 |
220 | @i[
221 | (define-syntax (quoted-foo stx)
222 | #'"I am also foo, using #' instead of syntax")
223 | (quoted-foo)
224 | ]
225 |
226 | We'll use the @litchar{#'} shorthand from now on.
227 |
228 | Of course, we can emit syntax that is more interesting than a
229 | string literal. How about returning @racket[(displayln "hi")]?
230 |
231 | @i[
232 | (define-syntax (say-hi stx)
233 | #'(displayln "hi"))
234 | (say-hi)
235 | ]
236 |
237 | When Racket expands our program, it sees the occurrence of
238 | @racket[(say-hi)], and sees it has a transformer function for that. It
239 | calls our function with the old syntax, and we return the new syntax,
240 | which is used to evaluate and run our program.
241 |
242 | @; ----------------------------------------------------------------------------
243 |
244 | @subsection{What's the input?}
245 |
246 | Our examples so far have ignored the input syntax and output some
247 | fixed syntax. But typically we will want to transform the input syntax
248 | into something else.
249 |
250 | Let's start by looking closely at what the input actually @italic{is}:
251 |
252 | @i[
253 | (define-syntax (show-me stx)
254 | (print stx)
255 | #'(void))
256 | (show-me '(+ 1 2))
257 | ]
258 |
259 | The @racket[(print stx)] shows what our transformer is given: a syntax
260 | object.
261 |
262 | A syntax object consists of several things. The first part is the
263 | S-expression representing the code, such as @racket['(+ 1 2)].
264 |
265 | Racket syntax is also decorated with some interesting information such
266 | as the source file, line number, and column. Finally, it has
267 | information about lexical scoping (which you don't need to worry about
268 | now, but will turn out to be important later.)
269 |
270 | There are a variety of functions available to access a syntax object.
271 | Let's define a piece of syntax:
272 |
273 | @i[
274 | (define stx #'(if x (list "true") #f))
275 | stx
276 | ]
277 |
278 | Now let's use functions that access the syntax object. The source
279 | information functions are:
280 |
281 | @margin-note{@racket[(syntax-source stx)] is returning @racket['eval],
282 | only because of how I'm generating this documentation, using an
283 | evaluator to run code snippets in Scribble. Normally this would be
284 | something like "my-file.rkt".}
285 |
286 | @i[
287 | (syntax-source stx)
288 | (syntax-line stx)
289 | (syntax-column stx)
290 | ]
291 |
292 | More interesting is the syntax "stuff" itself. @racket[syntax->datum]
293 | converts it completely into an S-expression:
294 |
295 | @i[
296 | (syntax->datum stx)
297 | ]
298 |
299 | Whereas @racket[syntax-e] only goes "one level down". It may return a
300 | list that has syntax objects:
301 |
302 | @i[
303 | (syntax-e stx)
304 | ]
305 |
306 | Each of those syntax objects could be converted by @racket[syntax-e],
307 | and so on recursively---which is what @racket[syntax->datum] does.
308 |
309 | In most cases, @racket[syntax->list] gives the same result as
310 | @racket[syntax-e]:
311 |
312 | @i[
313 | (syntax->list stx)
314 | ]
315 |
316 | (When would @racket[syntax-e] and @racket[syntax->list] differ? Let's
317 | not get side-tracked now.)
318 |
319 | When we want to transform syntax, we'll generally take the pieces we
320 | were given, maybe rearrange their order, perhaps change some of the
321 | pieces, and often introduce brand-new pieces.
322 |
323 |
324 | @; ----------------------------------------------------------------------------
325 |
326 | @subsection{Actually transforming the input}
327 |
328 | Let's write a transformer function that reverses the syntax it was
329 | given:
330 |
331 | @margin-note{The @racket[values] at the end of the example allows the
332 | result to evaluate nicely. Try
333 | @racket[(reverse-me "backwards" "am" "i")] to see why it's handy.}
334 | @i[
335 | (define-syntax (reverse-me stx)
336 | (datum->syntax stx (reverse (cdr (syntax->datum stx)))))
337 | (reverse-me "backwards" "am" "i" values)
338 | ]
339 |
340 | Understand Yoda, we can. Great, but how does this work?
341 |
342 | First we take the input syntax, and give it to
343 | @racket[syntax->datum]. This converts the syntax into a plain old
344 | list:
345 |
346 | @i[
347 | (syntax->datum #'(reverse-me "backwards" "am" "i" values))
348 | ]
349 |
350 | Using @racket[cdr] slices off the first item of the list,
351 | @racket[reverse-me], leaving the remainder:
352 | @racket[("backwards" "am" "i" values)]. Passing that to
353 | @racket[reverse] changes it to @racket[(values "i" "am" "backwards")]:
354 |
355 | @i[
356 | (reverse (cdr '(reverse-me "backwards" "am" "i" values)))
357 | ]
358 |
359 | Finally we use @racket[datum->syntax] to convert this back to
360 | @racket[syntax]:
361 |
362 | @i[
363 | (datum->syntax #f '(values "i" "am" "backwards"))
364 | ]
365 |
366 | That's what our transformer function gives back to the Racket
367 | compiler, and @italic{that} syntax is evaluated:
368 |
369 | @i[
370 | (values "i" "am" "backwards")
371 | ]
372 |
373 | @margin-note{The first argument of @racket[datum->syntax] contains the lexical
374 | context information that we want to associate with the @racket[syntax]
375 | outputted by the transformer. If the first argument is set to @racket[#f] then
376 | no lexical context will be associated.}
377 |
378 | @; ----------------------------------------------------------------------------
379 |
380 | @subsection{Compile time vs. run time}
381 |
382 | @codeblock0{
383 | (define-syntax (foo stx)
384 | (make-pipe) ;Ce n'est pas le temps d'exécution
385 | #'(void))
386 | }
387 |
388 | Normal Racket code runs at ... run time. Duh.
389 |
390 | @margin-note{Instead of "compile time vs. run time", you may hear it
391 | described as "syntax phase vs. runtime phase". Same difference.}
392 |
393 | But a syntax transformer is called by Racket as part of the process of
394 | parsing, expanding, and compiling our program. In other words, our
395 | syntax transformer function is evaluated at compile time.
396 |
397 | This aspect of macros lets you do things that simply aren't possible
398 | in normal code. One of the classic examples is something like the
399 | Racket form, @racket[if]:
400 |
401 | @racket[(if )]
402 |
403 | If we implemented @racket[if] as a function, all of the arguments
404 | would be evaluated before being provided to the function.
405 |
406 | @i[
407 | (define (our-if condition true-expr false-expr)
408 | (cond [condition true-expr]
409 | [else false-expr]))
410 | (our-if #t
411 | "true"
412 | "false")
413 | ]
414 |
415 | That seems to work. However, how about this:
416 |
417 | @i[
418 | (define (display-and-return x)
419 | (displayln x)
420 | x)
421 | (our-if #t
422 | (display-and-return "true")
423 | (display-and-return "false"))
424 | ]
425 |
426 | @margin-note{One answer is that functional programming is good, and
427 | side-effects are bad. But avoiding side-effects isn't always
428 | practical.}
429 |
430 | Oops. Because the expressions have a side-effect, it's obvious that
431 | they are both evaluated. And that could be a problem---what if the
432 | side-effect includes deleting a file on disk? You wouldn't want
433 | @racket[(if user-wants-file-deleted? (delete-file) (void))] to delete
434 | a file even when @racket[user-wants-file-deleted?] is @racket[#f].
435 |
436 | So this simply can't work as a plain function. However a syntax
437 | transformer can rearrange the syntax -- rewrite the code -- at compile
438 | time. The pieces of syntax are moved around, but they aren't actually
439 | evaluated until run time.
440 |
441 | Here is one way to do this:
442 |
443 | @i[
444 | (define-syntax (our-if-v2 stx)
445 | (define xs (syntax->list stx))
446 | (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)]
447 | [else ,(cadddr xs)])))
448 | (our-if-v2 #t
449 | (display-and-return "true")
450 | (display-and-return "false"))
451 | (our-if-v2 #f
452 | (display-and-return "true")
453 | (display-and-return "false"))
454 | ]
455 |
456 | That gave the right answer. But how? Let's pull out the transformer
457 | function itself, and see what it did. We start with an example of some
458 | input syntax:
459 |
460 | @i[
461 | (define stx (syntax (our-if-v2 #t "true" "false")))
462 | (displayln stx)
463 | ]
464 |
465 | 1. We take the original syntax, and use @racket[syntax->list] to
466 | change it into a @racket[list] of syntax objects:
467 |
468 | @i[
469 | (define xs (syntax->list stx))
470 | (displayln xs)
471 | ]
472 |
473 | 2. To change this into a Racket @racket[cond] form, we need to take
474 | the three interesting pieces---the condition, true-expression, and
475 | false-expression---from the list using @racket[cadr], @racket[caddr],
476 | and @racket[cadddr] and arrange them into a @racket[cond] form:
477 |
478 | @racketblock[
479 | `(cond [,(cadr xs) ,(caddr xs)]
480 | [else ,(cadddr xs)])
481 | ]
482 |
483 | 3. Finally, we change that into @racket[syntax] using
484 | @racket[datum->syntax]:
485 |
486 | @i[
487 | (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)]
488 | [else ,(cadddr xs)]))
489 | ]
490 |
491 | So that works, but using @racket[cadddr] etc. to destructure a list is
492 | painful and error-prone. Maybe you know Racket's @racket[match]?
493 | Using that would let us do pattern-matching.
494 |
495 | @margin-note{Notice that we don't care about the first item in the
496 | syntax list. We didn't take @racket[(car xs)] in our-if-v2, and we
497 | didn't use @racket[name] when we used pattern-matching. In general, a
498 | syntax transformer won't care about that, because it is the name of
499 | the transformer binding. In other words, a macro usually doesn't care
500 | about its own name.}
501 |
502 | Instead of:
503 |
504 | @i[
505 | (define-syntax (our-if-v2 stx)
506 | (define xs (syntax->list stx))
507 | (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)]
508 | [else ,(cadddr xs)])))
509 | ]
510 |
511 | We can write:
512 |
513 | @i[
514 | (define-syntax (our-if-using-match stx)
515 | (match (syntax->list stx)
516 | [(list name condition true-expr false-expr)
517 | (datum->syntax stx `(cond [,condition ,true-expr]
518 | [else ,false-expr]))]))]
519 |
520 | Great. Now let's try using it:
521 |
522 | @i[
523 | (our-if-using-match #t "true" "false")
524 | ]
525 |
526 | Oops. It's complaining that @racket[match] isn't defined.
527 |
528 | Our transformer function is working at compile time, not run time. And
529 | at compile time, only @racket[racket/base] is required for you
530 | automatically---not the full @racket[racket].
531 |
532 | Anything beyond @racket[racket/base], we have to require
533 | ourselves---and require it for compile time using the
534 | @racket[for-syntax] form of @racket[require].
535 |
536 | In this case, instead of using plain @racket[(require racket/match)],
537 | we want @racket[(require (for-syntax racket/match))]---the
538 | @racket[for-syntax] part meaning, "for compile time".
539 |
540 | So let's try that:
541 |
542 | @i[
543 | (require (for-syntax racket/match))
544 | (define-syntax (our-if-using-match-v2 stx)
545 | (match (syntax->list stx)
546 | [(list _ condition true-expr false-expr)
547 | (datum->syntax stx `(cond [,condition ,true-expr]
548 | [else ,false-expr]))]))
549 | (our-if-using-match-v2 #t "true" "false")
550 | ]
551 |
552 | Joy.
553 |
554 | @; ----------------------------------------------------------------------------
555 |
556 | @subsection{@racket[begin-for-syntax]}
557 |
558 | We used @racket[for-syntax] to @racket[require] the
559 | @racket[racket/match] module because we needed to use @racket[match]
560 | at compile time.
561 |
562 | What if we wanted to define our own helper function to be used by a
563 | macro? One way to do that is put it in another module, and
564 | @racket[require] it using @racket[for-syntax], just like we did with
565 | the @racket[racket/match] module.
566 |
567 | If instead we want to put the helper in the same module, we can't
568 | simply @racket[define] it and use it---the definition would exist at
569 | run time, but we need it at compile time. The answer is to put the
570 | definition of the helper function(s) inside @racket[begin-for-syntax]:
571 |
572 | @racketblock[
573 | (begin-for-syntax
574 | (define (my-helper-function ....)
575 | ....))
576 | (define-syntax (macro-using-my-helper-function stx)
577 | (my-helper-function ....)
578 | ....)
579 | ]
580 |
581 | In the simple case, we can also use @racket[define-for-syntax], which
582 | composes @racket[begin-for-syntax] and @racket[define]:
583 |
584 | @racketblock[
585 | (define-for-syntax (my-helper-function ....)
586 | ....)
587 | (define-syntax (macro-using-my-helper-function stx)
588 | (my-helper-function ....)
589 | ....)
590 | ]
591 |
592 | To review:
593 |
594 | @itemize[
595 |
596 | @item{Syntax transformers work at compile time, not run time. The good
597 | news is this means we can do things like rearrange the pieces of
598 | syntax without evaluating them. We can implement forms like
599 | @racket[if] that simply couldn't work properly as run time functions.}
600 |
601 | @item{More good news is that there isn't some special, weird language
602 | for writing syntax transformers. We can write these transformer
603 | functions using the Racket language we already know and love.}
604 |
605 | @item{The semi-bad news is that the familiarity can make it easy to forget
606 | that we're not working at run time. Sometimes that's important to
607 | remember.
608 |
609 | @itemize[
610 |
611 | @item{For example only @racket[racket/base] is required for us
612 | automatically. If we need other modules, we have to require them, and
613 | do so @italic{for compile time} using @racket[for-syntax].}
614 |
615 | @item{Similarly, if we want to define helper functions in the same
616 | file/module as the macros that use them, we need to wrap the
617 | definitions inside a @racket[begin-for-syntax] form. Doing so makes
618 | them available at compile time.}
619 |
620 | ]
621 | }
622 | ]
623 |
624 | @; ----------------------------------------------------------------------------
625 | @; ----------------------------------------------------------------------------
626 |
627 | @section[#:tag "pattern-matching"]{Pattern matching: syntax-case and syntax-rules}
628 |
629 | Most useful syntax transformers work by taking some input syntax, and
630 | rearranging the pieces into something else. As we saw, this is
631 | possible but tedious using list accessors such as
632 | @racket[cadddr]. It's more convenient and less error-prone to use
633 | @racket[match] to do pattern-matching.
634 |
635 | @margin-note{Historically, @racket[syntax-case] and
636 | @racket[syntax-rules] pattern matching came first. @racket[match] was
637 | added to Racket later.}
638 |
639 | It turns out that pattern-matching was one of the first improvements
640 | to be added to the Racket macro system. It's called
641 | @racket[syntax-case], and has a shorthand for simple situations called
642 | @racket[define-syntax-rule].
643 |
644 | Recall our previous example:
645 |
646 | @racketblock[
647 | (require (for-syntax racket/match))
648 | (define-syntax (our-if-using-match-v2 stx)
649 | (match (syntax->list stx)
650 | [(list _ condition true-expr false-expr)
651 | (datum->syntax stx `(cond [,condition ,true-expr]
652 | [else ,false-expr]))]))
653 | ]
654 |
655 | Here's what it looks like using @racket[syntax-case]:
656 |
657 | @i[
658 | (define-syntax (our-if-using-syntax-case stx)
659 | (syntax-case stx ()
660 | [(_ condition true-expr false-expr)
661 | #'(cond [condition true-expr]
662 | [else false-expr])]))
663 | (our-if-using-syntax-case #t "true" "false")
664 | ]
665 |
666 | Pretty similar, huh? The pattern matching part looks almost exactly
667 | the same. The way we specify the new syntax is simpler. We don't need
668 | to do quasi-quoting and unquoting. We don't need to use
669 | @racket[datum->syntax]. Instead, we supply a "template", which uses
670 | variables from the pattern.
671 |
672 | There is a shorthand for simple pattern-matching cases, which expands
673 | into @racket[syntax-case]. It's called @racket[define-syntax-rule]:
674 |
675 | @i[
676 | (define-syntax-rule (our-if-using-syntax-rule condition true-expr false-expr)
677 | (cond [condition true-expr]
678 | [else false-expr]))
679 | (our-if-using-syntax-rule #t "true" "false")
680 | ]
681 |
682 | Here's the thing about @racket[define-syntax-rule]. Because it's so
683 | simple, @racket[define-syntax-rule] is often the first thing people are
684 | taught about macros. But it's almost deceptively simple. It looks so
685 | much like defining a normal run time function---yet it's not. It's
686 | working at compile time, not run time. Worse, the moment you want to
687 | do more than @racket[define-syntax-rule] can handle, you can fall off
688 | a cliff into what feels like complicated and confusing
689 | territory. Hopefully, because we started with a basic syntax
690 | transformer, and worked up from that, we won't have that problem. We
691 | can appreciate @racket[define-syntax-rule] as a convenient shorthand,
692 | but not be scared of, or confused about, that for which it's
693 | shorthand.
694 |
695 | Most of the materials I found for learning macros, including the
696 | Racket @italic{Guide}, do a very good job explaining
697 | @hyperlink["http://docs.racket-lang.org/guide/pattern-macros.html" "how
698 | patterns and templates work"]. So I won't regurgitate that here.
699 |
700 | Sometimes, we need to go a step beyond the pattern and template. Let's
701 | look at some examples, how we can get confused, and how to get it
702 | working.
703 |
704 | @; ----------------------------------------------------------------------------
705 |
706 | @subsection{Pattern variable vs. template---fight!}
707 |
708 | Let's say we want to define a function with a hyphenated name, a-b,
709 | but we supply the a and b parts separately. The Racket @racket[struct]
710 | macro does something like this: @racket[(struct foo (field1 field2))]
711 | automatically defines a number of functions whose names are variations
712 | on the name @racket[foo]---such as @racket[foo-field1],
713 | @racket[foo-field2], @racket[foo?], and so on.
714 |
715 | So let's pretend we're doing something like that. We want to transform
716 | the syntax @racket[(hyphen-define a b (args) body)] to the syntax
717 | @racket[(define (a-b args) body)].
718 |
719 | A wrong first attempt is:
720 |
721 | @i[
722 | (define-syntax (hyphen-define/wrong1 stx)
723 | (syntax-case stx ()
724 | [(_ a b (args ...) body0 body ...)
725 | (let ([name (string->symbol (format "~a-~a" a b))])
726 | #'(define (name args ...)
727 | body0 body ...))]))
728 | ]
729 |
730 | Huh. We have no idea what this error message means. Well, let's try to
731 | work it out. The "template" the error message refers to is the
732 | @racket[#'(define (name args ...) body0 body ...)] portion. The
733 | @racket[let] isn't part of that template. It sounds like we can't use
734 | @racket[a] (or @racket[b]) in the @racket[let] part.
735 |
736 | In fact, @racket[syntax-case] can have as many templates as you
737 | want. The obvious, required template is the final expression supplying
738 | the output syntax. But you can use @racket[syntax] (a.k.a. @litchar{#'}) on a
739 | pattern variable. This makes another template, albeit a small, "fun
740 | size" template. Let's try that:
741 |
742 | @i[
743 | (define-syntax (hyphen-define/wrong1.1 stx)
744 | (syntax-case stx ()
745 | [(_ a b (args ...) body0 body ...)
746 | (let ([name (string->symbol (format "~a-~a" #'a #'b))])
747 | #'(define (name args ...)
748 | body0 body ...))]))
749 | ]
750 |
751 | No more errors---good! Let's try to use it:
752 |
753 | @i[
754 | (hyphen-define/wrong1.1 foo bar () #t)
755 | (foo-bar)
756 | ]
757 |
758 | Apparently our macro is defining a function with some name other than
759 | @racket[foo-bar]. Huh.
760 |
761 | This is where the Macro Stepper in DrRacket is
762 | invaluable. @margin-note{Even if you prefer mostly to use Emacs, this
763 | is a situation where it's definitely worth temporarily using DrRacket
764 | for its Macro Stepper.}
765 |
766 | @image[#:scale 0.5 "macro-stepper.png"]
767 |
768 | The Macro Stepper says that the use of our macro:
769 |
770 | @racketblock[
771 | (hyphen-define/wrong1.1 foo bar () #t)
772 | ]
773 |
774 | expanded to:
775 |
776 | @racketblock[
777 | (define (name) #t)
778 | ]
779 |
780 | Well that explains it. Instead, we wanted to expand to:
781 |
782 | @racketblock[
783 | (define (foo-bar) #t)
784 | ]
785 |
786 | Our template is using the symbol @racket[name] but we wanted its
787 | value, such as @racket[foo-bar] in this use of our macro.
788 |
789 | Is there anything we already know that behaves like this---where using
790 | a variable in the template yields its value? Yes: Pattern
791 | variables. Our pattern doesn't include @racket[name] because we don't
792 | expect it in the original syntax---indeed the whole point of this
793 | macro is to create it. So @racket[name] can't be in the main
794 | pattern. Fine---let's make an @italic{additional} pattern. We can do
795 | that using an additional, nested @racket[syntax-case]:
796 |
797 | @i[
798 | (define-syntax (hyphen-define/wrong1.2 stx)
799 | (syntax-case stx ()
800 | [(_ a b (args ...) body0 body ...)
801 | (syntax-case (datum->syntax #'a
802 | (string->symbol (format "~a-~a" #'a #'b)))
803 | ()
804 | [name #'(define (name args ...)
805 | body0 body ...)])]))
806 | ]
807 |
808 | Looks weird? Let's take a deep breath. Normally our transformer
809 | function is given syntax by Racket, and we pass that syntax to
810 | @racket[syntax-case]. But we can also create some syntax of our own,
811 | on the fly, and pass @italic{that} to @racket[syntax-case]. That's all
812 | we're doing here. The whole @racket[(datum->syntax ...)] expression is
813 | syntax that we're creating on the fly. We can give that to
814 | @racket[syntax-case], and match it using a pattern variable named
815 | @racket[name]. Voila, we have a new pattern variable. We can use it in
816 | a template, and its value will go in the template.
817 |
818 | We might have one more---just one, I promise!---small problem left.
819 | Let's try to use our new version:
820 |
821 | @i[
822 | (hyphen-define/wrong1.2 foo bar () #t)
823 | (foo-bar)
824 | ]
825 |
826 | Hmm. @racket[foo-bar] is @italic{still} not defined. Back to the Macro
827 | Stepper. It says now we're expanding to:
828 |
829 | @racketblock[(define (|#-#|) #t)]
830 |
831 | Oh right: @racket[#'a] and @racket[#'b] are syntax objects. Therefore
832 |
833 | @racketblock[(string->symbol (format "~a-~a" #'a #'b))]
834 |
835 | is the printed form of both syntax objects, joined by a hyphen:
836 |
837 | @racketblock[|#-#|]
838 |
839 | Instead we want the datum in the syntax objects, such as the symbols
840 | @racket[foo] and @racket[bar]. Which we get using
841 | @racket[syntax->datum]:
842 |
843 | @i[
844 | (define-syntax (hyphen-define/ok1 stx)
845 | (syntax-case stx ()
846 | [(_ a b (args ...) body0 body ...)
847 | (syntax-case (datum->syntax #'a
848 | (string->symbol (format "~a-~a"
849 | (syntax->datum #'a)
850 | (syntax->datum #'b))))
851 | ()
852 | [name #'(define (name args ...)
853 | body0 body ...)])]))
854 | (hyphen-define/ok1 foo bar () #t)
855 | (foo-bar)
856 | ]
857 |
858 | And now it works!
859 |
860 | Next, some shortcuts.
861 |
862 | @subsubsection{@racket[with-syntax]}
863 |
864 | Instead of an additional, nested @racket[syntax-case], we could use
865 | @racket[with-syntax]@margin-note*{Another name for
866 | @racket[with-syntax] could be, "with new pattern variable".}. This
867 | rearranges the @racket[syntax-case] to look more like a @racket[let]
868 | statement---first the name, then the value. Also it's more convenient
869 | if we need to define more than one pattern variable.
870 |
871 | @i[
872 | (define-syntax (hyphen-define/ok2 stx)
873 | (syntax-case stx ()
874 | [(_ a b (args ...) body0 body ...)
875 | (with-syntax ([name (datum->syntax #'a
876 | (string->symbol (format "~a-~a"
877 | (syntax->datum #'a)
878 | (syntax->datum #'b))))])
879 | #'(define (name args ...)
880 | body0 body ...))]))
881 | (hyphen-define/ok2 foo bar () #t)
882 | (foo-bar)
883 | ]
884 |
885 | Again, @racket[with-syntax] is simply @racket[syntax-case] rearranged:
886 |
887 | @racketblock[
888 | (syntax-case #,(italic "") () [#,(bold "") ])
889 | (with-syntax ([#,(bold "") #,(italic "")]) )
890 | ]
891 |
892 | Whether you use an additional @racket[syntax-case] or use
893 | @racket[with-syntax], either way you are simply defining additional
894 | pattern variables. Don't let the terminology and structure make it
895 | seem mysterious.
896 |
897 | @subsubsection{@racket[with-syntax*]}
898 |
899 | We know that @racket[let] doesn't let us use a binding in a subsequent
900 | one:
901 |
902 | @i[
903 | (let ([a 0]
904 | [b a])
905 | b)
906 | ]
907 |
908 | Instead we can nest @racket[let]s:
909 |
910 | @i[
911 | (let ([a 0])
912 | (let ([b a])
913 | b))
914 | ]
915 |
916 | Or use a shorthand for nesting, @racket[let*]:
917 |
918 | @i[
919 | (let* ([a 0]
920 | [b a])
921 | b)
922 | ]
923 |
924 | Similarly, instead of writing nested @racket[with-syntax]s, we can use
925 | @racket[with-syntax*]:
926 |
927 | @i[
928 | (require (for-syntax racket/syntax))
929 | (define-syntax (foo stx)
930 | (syntax-case stx ()
931 | [(_ a)
932 | (with-syntax* ([b #'a]
933 | [c #'b])
934 | #'c)]))
935 | ]
936 |
937 | One gotcha is that @racket[with-syntax*] isn't provided by
938 | @racket[racket/base]. We must @racket[(require (for-syntax
939 | racket/syntax))]. Otherwise we may get a rather bewildering error
940 | message:
941 |
942 | @italic{@tt{...: ellipses not allowed as an expression in: ...}}.
943 |
944 |
945 | @subsubsection{@racket[format-id]}
946 |
947 | There is a utility function in @racket[racket/syntax] called
948 | @racket[format-id] that lets us format identifier names more
949 | succinctly than what we did above:
950 |
951 | @i[
952 | (require (for-syntax racket/syntax))
953 | (define-syntax (hyphen-define/ok3 stx)
954 | (syntax-case stx ()
955 | [(_ a b (args ...) body0 body ...)
956 | (with-syntax ([name (format-id #'a "~a-~a" #'a #'b)])
957 | #'(define (name args ...)
958 | body0 body ...))]))
959 | (hyphen-define/ok3 bar baz () #t)
960 | (bar-baz)
961 | ]
962 |
963 | Using @racket[format-id] is convenient as it handles the tedium of
964 | converting from syntax to symbol datum to string ... and all the way
965 | back.
966 |
967 | The first argument of @racket[format-id], @racket[lctx], is the
968 | lexical context of the identifier that will be created. You almost
969 | never want to supply @racket[stx]---the overall chunk of syntax that
970 | the macro transforms. Instead you want to supply some more-specific
971 | bit of syntax, such as an identifier that the user has provided to the
972 | macro. In this example, we're using @racket[#'a]. The resulting
973 | identifier will have the same scope as that which the user provided.
974 | This is more likely to behave as the user expects, especially when our
975 | macro is composed with other macros.
976 |
977 | @subsubsection{Another example}
978 |
979 | Finally, here's a variation that accepts an arbitrary number of name
980 | parts to be joined with hyphens:
981 |
982 | @i[
983 | (require (for-syntax racket/string racket/syntax))
984 | (define-syntax (hyphen-define* stx)
985 | (syntax-case stx ()
986 | [(_ (names ...) (args ...) body0 body ...)
987 | (let ([name-stxs (syntax->list #'(names ...))])
988 | (with-syntax ([name (datum->syntax (car name-stxs)
989 | (string->symbol
990 | (string-join (for/list ([name-stx name-stxs])
991 | (symbol->string
992 | (syntax-e name-stx)))
993 | "-")))])
994 | #'(define (name args ...)
995 | body0 body ...)))]))
996 | (hyphen-define* (foo bar baz) (v) (* 2 v))
997 | (foo-bar-baz 50)
998 | ]
999 |
1000 | Just as when we used @racket[format-id], when using
1001 | @racket[datum->syntax] we're being careful with the first,
1002 | @racket[lctx] argument. We want the identifier we create to use the
1003 | lexical context of an identifier provided to the macro by the user. In
1004 | this case, the user's identifiers are in the @racket[(names ...)]
1005 | template variable. We change this from one @racket[syntax] into a
1006 | @racket[list] of @racket[syntax]es. The first element we use for the
1007 | lexical context. Then of course we'll use all the elements to form the
1008 | hyphenated identifier.
1009 |
1010 | To review:
1011 |
1012 | @itemize[
1013 |
1014 | @item{You can't use a pattern variable outside of a template. But
1015 | you can use @racket[syntax] or @litchar{#'} on a pattern variable to make
1016 | an ad hoc, "fun size" template.}
1017 |
1018 | @item{If you want to munge pattern variables for use in the
1019 | template, @racket[with-syntax] is your friend, because it lets you
1020 | create new pattern variables.}
1021 |
1022 | @item{Usually you'll need to use @racket[syntax->datum] to get the
1023 | interesting value inside.}
1024 |
1025 | @item{@racket[format-id] is convenient for formatting identifier
1026 | names.}
1027 |
1028 | ]
1029 |
1030 | @; ----------------------------------------------------------------------------
1031 |
1032 | @subsection{Making our own @racket[struct]}
1033 |
1034 | Let's apply what we just learned to a more-realistic example. We'll
1035 | pretend that Racket doesn't already have a @racket[struct]
1036 | capability. Fortunately, we can write a macro to provide our own
1037 | system for defining and using structures. To keep things simple, our
1038 | structure will be immutable (read-only) and it won't support
1039 | inheritance.
1040 |
1041 | Given a structure declaration like:
1042 |
1043 | @racketblock[
1044 | (our-struct name (field1 field2 ...))
1045 | ]
1046 |
1047 | We need to define some procedures:
1048 |
1049 | @itemize[
1050 |
1051 | @item{A constructor procedure whose name is the struct name. We'll
1052 | represent structures as a @racket[vector]. The structure name will be
1053 | element zero. The fields will be elements one onward.}
1054 |
1055 | @item{A predicate, whose name is the struct name with @tt{?}
1056 | appended.}
1057 |
1058 | @item{For each field, an accessor procedure to get its value. These
1059 | will be named struct-field (the name of the struct, a hyphen, and the
1060 | field name).}
1061 |
1062 | ]
1063 |
1064 |
1065 | @#reader scribble/comment-reader
1066 | (i
1067 | (require (for-syntax racket/syntax))
1068 | (define-syntax (our-struct stx)
1069 | (syntax-case stx ()
1070 | [(_ id (fields ...))
1071 | (with-syntax ([pred-id (format-id #'id "~a?" #'id)])
1072 | #`(begin
1073 | ;; Define a constructor.
1074 | (define (id fields ...)
1075 | (apply vector (cons (quote id) (list fields ...))))
1076 | ;; Define a predicate.
1077 | (define (pred-id v)
1078 | (and (vector? v)
1079 | (eq? (vector-ref v 0) 'id)))
1080 | ;; Define an accessor for each field.
1081 | #,@(for/list ([x (syntax->list #'(fields ...))]
1082 | [n (in-naturals 1)])
1083 | (with-syntax ([acc-id (format-id #'id "~a-~a" #'id x)]
1084 | [ix n])
1085 | #`(define (acc-id v)
1086 | (unless (pred-id v)
1087 | (error 'acc-id "~a is not a ~a struct" v 'id))
1088 | (vector-ref v ix))))))]))
1089 |
1090 | ;; Test it out
1091 | (require rackunit)
1092 | (our-struct foo (a b))
1093 | (define s (foo 1 2))
1094 | (check-true (foo? s))
1095 | (check-false (foo? 1))
1096 | (check-equal? (foo-a s) 1)
1097 | (check-equal? (foo-b s) 2)
1098 | (check-exn exn:fail?
1099 | (lambda () (foo-a "furble")))
1100 |
1101 | ;; The tests passed.
1102 | ;; Next, what if someone tries to declare:
1103 | (our-struct "blah" ("blah" "blah"))
1104 | )
1105 |
1106 | The error message is not very helpful. It's coming from
1107 | @racket[format-id], which is a private implementation detail of our macro.
1108 |
1109 | You may know that a @racket[syntax-case] clause can take an
1110 | optional "guard" or "fender" expression. Instead of
1111 |
1112 | @racketblock[
1113 | [pattern template]
1114 | ]
1115 |
1116 | It can be:
1117 |
1118 | @racketblock[
1119 | [pattern guard template]
1120 | ]
1121 |
1122 | Let's add a guard expression to our clause:
1123 |
1124 | @#reader scribble/comment-reader
1125 | (i
1126 | (require (for-syntax racket/syntax))
1127 | (define-syntax (our-struct stx)
1128 | (syntax-case stx ()
1129 | [(_ id (fields ...))
1130 | ;; Guard or "fender" expression:
1131 | (for-each (lambda (x)
1132 | (unless (identifier? x)
1133 | (raise-syntax-error #f "not an identifier" stx x)))
1134 | (cons #'id (syntax->list #'(fields ...))))
1135 | (with-syntax ([pred-id (format-id #'id "~a?" #'id)])
1136 | #`(begin
1137 | ;; Define a constructor.
1138 | (define (id fields ...)
1139 | (apply vector (cons (quote id) (list fields ...))))
1140 | ;; Define a predicate.
1141 | (define (pred-id v)
1142 | (and (vector? v)
1143 | (eq? (vector-ref v 0) 'id)))
1144 | ;; Define an accessor for each field.
1145 | #,@(for/list ([x (syntax->list #'(fields ...))]
1146 | [n (in-naturals 1)])
1147 | (with-syntax ([acc-id (format-id #'id "~a-~a" #'id x)]
1148 | [ix n])
1149 | #`(define (acc-id v)
1150 | (unless (pred-id v)
1151 | (error 'acc-id "~a is not a ~a struct" v 'id))
1152 | (vector-ref v ix))))))]))
1153 |
1154 | ;; Now the same misuse gives a better error message:
1155 | (our-struct "blah" ("blah" "blah"))
1156 | )
1157 |
1158 | Later, we'll see how @racket[syntax-parse] makes it even easier to
1159 | check usage and provide helpful messages about mistakes.
1160 |
1161 |
1162 | @subsection[#:tag "hash.refs"]{Using dot notation for nested hash lookups}
1163 |
1164 | The previous two examples used a macro to define functions whose names
1165 | were made by joining identifiers provided to the macro. This example
1166 | does the opposite: The identifier given to the macro is split into
1167 | pieces.
1168 |
1169 | If you write programs for web services you deal with JSON, which is
1170 | represented in Racket by a @racket[jsexpr?]. JSON often has
1171 | dictionaries that contain other dictionaries. In a @racket[jsexpr?]
1172 | these are represented by nested @racket[hasheq] tables:
1173 |
1174 | @#reader scribble/comment-reader
1175 | (i
1176 | ; Nested `hasheq's typical of a jsexpr:
1177 | (define js (hasheq 'a (hasheq 'b (hasheq 'c "value"))))
1178 | )
1179 |
1180 | In JavaScript you can use dot notation:
1181 |
1182 | @codeblock{
1183 | foo = js.a.b.c;
1184 | }
1185 |
1186 | In Racket it's not so convenient:
1187 |
1188 | @racketblock[(hash-ref (hash-ref (hash-ref js 'a) 'b) 'c)]
1189 |
1190 | We can write a helper function to make this a bit cleaner:
1191 |
1192 | @#reader scribble/comment-reader
1193 | (i
1194 | ;; This helper function:
1195 | (define/contract (hash-refs h ks [def #f])
1196 | ((hash? (listof any/c)) (any/c) . ->* . any)
1197 | (with-handlers ([exn:fail? (const (cond [(procedure? def) (def)]
1198 | [else def]))])
1199 | (for/fold ([h h])
1200 | ([k (in-list ks)])
1201 | (hash-ref h k))))
1202 |
1203 | ;; Lets us say:
1204 | (hash-refs js '(a b c))
1205 | )
1206 |
1207 | That's better. Can we go even further and use a dot notation somewhat
1208 | like JavaScript?
1209 |
1210 | @#reader scribble/comment-reader
1211 | (i
1212 | ;; This macro:
1213 | (require (for-syntax racket/syntax))
1214 | (define-syntax (hash.refs stx)
1215 | (syntax-case stx ()
1216 | ;; If the optional `default' is missing, use #f.
1217 | [(_ chain)
1218 | #'(hash.refs chain #f)]
1219 | [(_ chain default)
1220 | (let* ([chain-str (symbol->string (syntax->datum #'chain))]
1221 | [ids (for/list ([str (in-list (regexp-split #rx"\\." chain-str))])
1222 | (format-id #'chain "~a" str))])
1223 | (with-syntax ([hash-table (car ids)]
1224 | [keys (cdr ids)])
1225 | #'(hash-refs hash-table 'keys default)))]))
1226 | ;; Gives us "sugar" to say this:
1227 | (hash.refs js.a.b.c)
1228 | ;; Try finding a key that doesn't exist:
1229 | (hash.refs js.blah)
1230 | ;; Try finding a key that doesn't exist, specifying the default:
1231 | (hash.refs js.blah 'did-not-exist)
1232 | )
1233 |
1234 | It works!
1235 |
1236 | We've started to appreciate that our macros should give helpful
1237 | messages when used in error. Let's try to do that here.
1238 |
1239 | @#reader scribble/comment-reader
1240 | (i
1241 | (require (for-syntax racket/syntax))
1242 | (define-syntax (hash.refs stx)
1243 | (syntax-case stx ()
1244 | ;; Check for no args at all
1245 | [(_)
1246 | (raise-syntax-error #f "Expected hash.key0[.key1 ...] [default]" stx)]
1247 | ;; If the optional `default' is missing, use #f.
1248 | [(_ chain)
1249 | #'(hash.refs chain #f)]
1250 | [(_ chain default)
1251 | (unless (identifier? #'chain)
1252 | (raise-syntax-error #f "Expected hash.key0[.key1 ...] [default]" stx #'chain))
1253 | (let* ([chain-str (symbol->string (syntax->datum #'chain))]
1254 | [ids (for/list ([str (in-list (regexp-split #rx"\\." chain-str))])
1255 | (format-id #'chain "~a" str))])
1256 | ;; Check that we have at least hash.key
1257 | (unless (and (>= (length ids) 2)
1258 | (not (eq? (syntax-e (cadr ids)) '||)))
1259 | (raise-syntax-error #f "Expected hash.key" stx #'chain))
1260 | (with-syntax ([hash-table (car ids)]
1261 | [keys (cdr ids)])
1262 | #'(hash-refs hash-table 'keys default)))]))
1263 |
1264 | ;; See if we catch each of the misuses
1265 | (hash.refs)
1266 | (hash.refs 0)
1267 | (hash.refs js)
1268 | (hash.refs js.)
1269 | )
1270 |
1271 | Not too bad. Of course, the version with error-checking is quite a bit
1272 | longer. Error-checking code generally tends to obscure the logic, and
1273 | does here. Fortunately we'll soon see how @racket[syntax-parse] can
1274 | help mitigate that, in much the same way as contracts in normal
1275 | Racket or types in Typed Racket.
1276 |
1277 | Maybe we're not convinced that writing @racket[(hash.refs js.a.b.c)]
1278 | is really clearer than @racket[(hash-refs js '(a b c))]. Maybe we
1279 | won't actually use this approach. But the Racket macro system makes it
1280 | a possible choice.
1281 |
1282 | @; ----------------------------------------------------------------------------
1283 | @; ----------------------------------------------------------------------------
1284 |
1285 | @section{Syntax parameters}
1286 |
1287 | "Anaphoric if" or "aif" is a popular macro example. Instead of writing:
1288 |
1289 | @racketblock[
1290 | (let ([tmp (big-long-calculation)])
1291 | (if tmp
1292 | (foo tmp)
1293 | #f))
1294 | ]
1295 |
1296 | You could write:
1297 |
1298 | @racketblock[
1299 | (aif (big-long-calculation)
1300 | (foo it)
1301 | #f)
1302 | ]
1303 |
1304 | In other words, when the condition is true, an @racket[it] identifier
1305 | is automatically created and set to the value of the condition. This
1306 | should be easy:
1307 |
1308 |
1309 | @i[
1310 | (define-syntax-rule (aif condition true-expr false-expr)
1311 | (let ([it condition])
1312 | (if it
1313 | true-expr
1314 | false-expr)))
1315 | (aif #t (displayln it) (void))
1316 | ]
1317 |
1318 | Wait, what? @racket[it] is undefined?
1319 |
1320 | It turns out that all along we have been protected from making a
1321 | certain kind of mistake in our macros. The mistake is if our new
1322 | syntax introduces a variable that accidentally conflicts with one in
1323 | the code surrounding our macro.
1324 |
1325 | The Racket @italic{Reference} section,
1326 | @hyperlink["http://docs.racket-lang.org/reference/syntax-model.html#(part._transformer-model)" "Transformer
1327 | Bindings"], has a good explanation and example. Basically, syntax
1328 | has "marks" to preserve lexical scope. This makes your macro behave
1329 | like a normal function, for lexical scoping.
1330 |
1331 | If a normal function defines a variable named @racket[x], it won't
1332 | conflict with a variable named @racket[x] in an outer scope:
1333 |
1334 | @i[
1335 | (let ([x "outer"])
1336 | (let ([x "inner"])
1337 | (printf "The inner `x' is ~s\n" x))
1338 | (printf "The outer `x' is ~s\n" x))
1339 | ]
1340 |
1341 | When our macros also respect lexical scoping, it's easier to write
1342 | reliable macros that behave predictably.
1343 |
1344 | So that's wonderful default behavior. But sometimes we want to
1345 | introduce a magic variable on purpose---such as @racket[it] for
1346 | @racket[aif].
1347 |
1348 | There's a bad way to do this and a good way.
1349 |
1350 | The bad way is to use @racket[datum->syntax], which is tricky to use correctly. @margin-note*{See @hyperlink["http://www.schemeworkshop.org/2011/papers/Barzilay2011.pdf" "Keeping it Clean with Syntax Parameters (PDF)"].}
1351 |
1352 | The good way is with a syntax parameter, using
1353 | @racket[define-syntax-parameter] and
1354 | @racket[syntax-parameterize]. You're probably familiar with regular
1355 | parameters in Racket:
1356 |
1357 | @i[
1358 | (define current-foo (make-parameter "some default value"))
1359 | (current-foo)
1360 | (parameterize ([current-foo "I have a new value, for now"])
1361 | (current-foo))
1362 | (current-foo)
1363 | ]
1364 |
1365 | That's a normal parameter. The syntax variation works similarly. The
1366 | idea is that we'll define @racket[it] to mean an error by
1367 | default. Only inside of our @racket[aif] will it have a meaningful
1368 | value:
1369 |
1370 | @i[
1371 | (require racket/stxparam)
1372 | (define-syntax-parameter it
1373 | (lambda (stx)
1374 | (raise-syntax-error (syntax-e stx) "can only be used inside aif")))
1375 | (define-syntax-rule (aif condition true-expr false-expr)
1376 | (let ([tmp condition])
1377 | (if tmp
1378 | (syntax-parameterize ([it (make-rename-transformer #'tmp)])
1379 | true-expr)
1380 | false-expr)))
1381 | (aif 10 (displayln it) (void))
1382 | (aif #f (displayln it) (void))
1383 | ]
1384 |
1385 | Inside the @racket[syntax-parameterize], @racket[it] acts as an alias
1386 | for @racket[tmp]. The alias behavior is created by
1387 | @racket[make-rename-transformer].
1388 |
1389 | If we try to use @racket[it] outside of an @racket[aif] form, and
1390 | @racket[it] isn't otherwise defined, we get an error like we want:
1391 |
1392 | @i[
1393 | (displayln it)
1394 | ]
1395 |
1396 | But we can still define @racket[it] as a normal variable in local
1397 | definition contexts like:
1398 |
1399 | @i[
1400 | (let ([it 10])
1401 | it)
1402 | ]
1403 |
1404 | or:
1405 |
1406 | @i[
1407 | (define (foo)
1408 | (define it 10)
1409 | it)
1410 | (foo)
1411 | ]
1412 |
1413 |
1414 | For a deeper look, see @hyperlink["http://www.schemeworkshop.org/2011/papers/Barzilay2011.pdf" "Keeping it Clean with Syntax Parameters"].
1415 |
1416 | @; ----------------------------------------------------------------------------
1417 | @; ----------------------------------------------------------------------------
1418 |
1419 | @section{What's the point of @racket[splicing-let]?}
1420 |
1421 | I stared at @racket[racket/splicing] for the longest time. What does
1422 | it do? Why would I use it? Why is it in the Macros section of the
1423 | reference?
1424 |
1425 | Step one, @elem[#:style "strike"]{cut a hole in the box}
1426 | de-mythologize it. For example, using @racket[splicing-let] like this:
1427 |
1428 | @#reader scribble/comment-reader
1429 | (i
1430 | (require racket/splicing)
1431 | (splicing-let ([x 0])
1432 | (define (get-x)
1433 | x))
1434 | ;; get-x is visible out here:
1435 | (get-x)
1436 | ;; but x is not:
1437 | x
1438 | )
1439 |
1440 | is equivalent to:
1441 |
1442 | @#reader scribble/comment-reader
1443 | (i
1444 | (define get-y
1445 | (let ([y 0])
1446 | (lambda ()
1447 | y)))
1448 | ;; get-y is visible out here:
1449 | (get-y)
1450 | ;; but y is not:
1451 | y
1452 | )
1453 |
1454 | This is the classic Lisp/Scheme/Racket idiom sometimes called "let
1455 | over lambda". @margin-note*{A
1456 | @hyperlink["http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html" "koan"]
1457 | about closures and objects.} A closure hides @racket[y], which can
1458 | only be accessed via @racket[get-y].
1459 |
1460 | So why would we care about the splicing forms? They can be more
1461 | concise, especially when there are multiple body forms:
1462 |
1463 | @i[
1464 | (require racket/splicing)
1465 | (splicing-let ([x 0])
1466 | (define (inc)
1467 | (set! x (+ x 1)))
1468 | (define (dec)
1469 | (set! x (- 1 x)))
1470 | (define (get)
1471 | x))
1472 | ]
1473 |
1474 | The splicing variation is more convenient than the usual way:
1475 |
1476 | @#reader scribble/comment-reader
1477 | (i
1478 | (define-values (inc dec get)
1479 | (let ([x 0])
1480 | (values (lambda () ;inc
1481 | (set! x (+ x 1)))
1482 | (lambda () ;dec
1483 | (set! x (- x 1)))
1484 | (lambda () ;get
1485 | x))))
1486 | )
1487 |
1488 | When there are many body forms---and we're generating them in a
1489 | macro---the splicing variations can be much easier.
1490 |
1491 | @; ----------------------------------------------------------------------------
1492 | @; ----------------------------------------------------------------------------
1493 |
1494 | @section{Robust macros: syntax-parse}
1495 |
1496 | Functions can be used in error. So can macros.
1497 |
1498 | @subsection{Error-handling strategies for functions}
1499 |
1500 | With plain old functions, we have several choices how to handle
1501 | misuse.
1502 |
1503 | 1. Don't check at all.
1504 |
1505 | @#reader scribble/comment-reader
1506 | (i
1507 | (define (misuse s)
1508 | (string-append s " snazzy suffix"))
1509 | ;; User of the function:
1510 | (misuse 0)
1511 | ;; I guess I goofed, but -- what is this "string-append" of which you
1512 | ;; speak??
1513 | )
1514 |
1515 | The problem is that the resulting error message will be confusing. Our
1516 | user thinks they're calling @racket[misuse], but they're getting an error
1517 | message from @racket[string-append]. In this simple example they
1518 | could probably guess what's happening, but in most cases they won't.
1519 |
1520 | 2. Write some error handling code.
1521 |
1522 | @#reader scribble/comment-reader
1523 | (i
1524 | (define (misuse s)
1525 | (unless (string? s)
1526 | (error 'misuse "expected a string, but got ~a" s))
1527 | (string-append s " snazzy suffix"))
1528 | ;; User of the function:
1529 | (misuse 0)
1530 | ;; I goofed, and understand why! It's a shame the writer of the
1531 | ;; function had to work so hard to tell me.
1532 | )
1533 |
1534 | Unfortunately the error code tends to overwhelm and/or obscure our
1535 | function definition. Also, the error message is good but not
1536 | great. Improving it would require even more error code.
1537 |
1538 | 3. Use a contract.
1539 |
1540 | @#reader scribble/comment-reader
1541 | (i
1542 | (define/contract (misuse s)
1543 | (string? . -> . string?)
1544 | (string-append s " snazzy suffix"))
1545 | ;; User of the function:
1546 | (misuse 0)
1547 | ;; I goofed, and understand why! I'm happier, and I hear the writer of
1548 | ;; the function is happier, too.
1549 | )
1550 |
1551 | This is the best of both worlds.
1552 |
1553 | The contract is simple and concise. Even better, it's
1554 | declarative. We say what we want to happen, not how.
1555 |
1556 | On the other hand the user of our function gets a very detailed error
1557 | message. Plus, the message is in a standard, familiar format.
1558 |
1559 | 4. Use Typed Racket.
1560 |
1561 | @codeblock{#lang typed/racket}
1562 | @interaction[#:eval typed/evaluator
1563 | (: misuse (String -> String))
1564 | (define (misuse s)
1565 | (string-append s " snazzy suffix"))
1566 | (misuse 0)
1567 | ]
1568 |
1569 | Even better, Typed Racket can catch usage mistakes up-front at compile
1570 | time.
1571 |
1572 | @subsection{Error-handling strategies for macros}
1573 |
1574 | For macros, we have similar choices.
1575 |
1576 | 1. Ignore the possibility of misuse. This choice is even worse for
1577 | macros. The default error messages are even less likely to make sense,
1578 | much less help our user know what to do.
1579 |
1580 | 2. Write error-handling code. We saw how much this complicated our
1581 | macros in our example of @secref["hash.refs"]. And while we're still
1582 | learning how to write macros, we especially don't want more cognitive
1583 | load and obfuscation.
1584 |
1585 | 3. Use @racket[syntax-parse]. For macros, this is the equivalent of
1586 | using contracts or types for functions. We can declare that input
1587 | pattern elements must be certain kinds of things, such as an
1588 | identifier. Instead of "types", the kinds are referred to as "syntax
1589 | classes". There are predefined syntax classes, plus we can define our
1590 | own.
1591 |
1592 | @subsection{Using @racket[syntax-parse]}
1593 |
1594 | November 1, 2012: So here's the deal. After writing everything up to
1595 | this point, I sat down to re-read the documentation for
1596 | @racket[syntax-parse]. It was...very understandable. I didn't feel
1597 | confused.
1598 |
1599 | Why? The documentation has a nice
1600 | @hyperlink["http://docs.racket-lang.org/syntax/stxparse-intro.html"]{Introduction}
1601 | with many simple examples, followed by an
1602 | @hyperlink["http://docs.racket-lang.org/syntax/stxparse-examples.html"]{Examples}
1603 | section illustrating many real-world scenarios.
1604 |
1605 | @italic{Update:} Furthermore, Ben Greenman has created a package whose
1606 | docs provide an excellent set of even more
1607 | @hyperlink["http://docs.racket-lang.org/syntax-parse-example/index.html"]{Syntax
1608 | Parse Examples}.
1609 |
1610 | Furthermore, everything I'd learned up to this point prepared me to
1611 | appreciate what @racket[syntax-parse] does, and why. The details of
1612 | how to use it seem pretty straightforward, so far.
1613 |
1614 | This might well be a temporary state of me "not knowing what I don't
1615 | know". As I dig in and use it more, maybe I'll discover something
1616 | confusing or tricky. If/when I do, I'll come back here and update
1617 | this.
1618 |
1619 | But for now I'll focus on improving the previous parts.
1620 |
1621 | @; ----------------------------------------------------------------------------
1622 | @; ----------------------------------------------------------------------------
1623 |
1624 | @section{References and Acknowledgments}
1625 |
1626 | Eli Barzilay's blog post,
1627 | @hyperlink["http://blog.racket-lang.org/2011/04/writing-syntax-case-macros.html" "Writing
1628 | ‘syntax-case’ Macros"], helped me understand many key details and
1629 | concepts, and inspired me to use a "bottom-up" approach.
1630 |
1631 | Eli wrote another blog post,
1632 | @hyperlink["http://blog.racket-lang.org/2008/02/dirty-looking-hygiene.html" "Dirty
1633 | Looking Hygiene"], which explains @racket[syntax-parameterize]. I
1634 | relied heavily on that, mostly just updating it since his post was
1635 | written before PLT Scheme was renamed to Racket.
1636 |
1637 | Matthew Flatt's
1638 | @hyperlink["http://www.cs.utah.edu/plt/publications/macromod.pdf" "Composable
1639 | and Compilable Macros: You Want it When? (PDF)"] explains how Racket
1640 | handles compile time vs. run time.
1641 |
1642 | @hyperlink["http://www.scheme.com/tspl4/syntax.html#./syntax:h0" "Chapter
1643 | 8"] of @italic{The Scheme Programming Language} by Kent Dybvig
1644 | explains @racket[syntax-rules] and @racket[syntax-case].
1645 |
1646 | @hyperlink["http://www.ccs.neu.edu/racket/pubs/icfp10-cf.pdf" "Fortifying
1647 | Macros (PDF)"] is the paper by Ryan Culpepper and Matthias Felleisen
1648 | introducing @racket[syntax-parse].
1649 |
1650 | Shriram Krishnamurthi looked at a very early draft and encouraged me
1651 | to keep going. Sam Tobin-Hochstadt and Robby Findler also encouraged
1652 | me. Matthew Flatt showed me how to make a Scribble
1653 | @racket[interaction] print @racket[syntax] as @racket["syntax"] rather
1654 | than as @racket["#'"]. Jay McCarthy helped me catch some mistakes and
1655 | confusions. Jon Rafkind provided suggestions. Kieron Hardy reported a
1656 | font issue and some typos.
1657 |
1658 | Finally, I noticed something strange. After writing much of this, when
1659 | I returned to some parts of the Racket documentation, I noticed it had
1660 | improved since I last read it. Of course, it was the same; I'd
1661 | changed. It's interesting how much of what we already know is
1662 | projected between the lines. My point is, the Racket documentation is
1663 | very good. The @italic{Guide} provides helpful examples and
1664 | tutorials. The @italic{Reference} is very clear and precise.
1665 |
--------------------------------------------------------------------------------