Various edits for prose and layouts.

This commit is contained in:
Greg Hendershott 2012-10-25 19:28:48 -04:00
parent 65e3eca65e
commit d554435088
2 changed files with 83 additions and 74 deletions

File diff suppressed because one or more lines are too long

View File

@ -130,8 +130,8 @@ YOU HAVE A SYNTAX TRANSFORMER
A syntax transformer is not one of the トランスフォーマ A syntax transformer is not one of the トランスフォーマ
@hyperlink["http://en.wikipedia.org/wiki/Transformers" "transformers"]. @hyperlink["http://en.wikipedia.org/wiki/Transformers" "transformers"].
Instead, it is quite simple. It is a function. The function takes Instead, it is simply a function. The function takes syntax and
syntax and returns syntax. It transforms syntax. returns syntax. It transforms syntax.
Here's a transformer function that ignores its input syntax, and Here's a transformer function that ignores its input syntax, and
always outputs syntax for a string literal: always outputs syntax for a string literal:
@ -198,13 +198,12 @@ calls our function with the old syntax, and we return the new syntax,
which is used to evaluate and run our program. which is used to evaluate and run our program.
@subsection{What is the input?} @subsection{What's the input, Kenneth?}
Our examples so far have been ignoring the input syntax, and Our examples so far ignored the input syntax, and output a fixed
outputting a fixed syntax. Usually, we want to transform the input to syntax. But usually we want to transform the input to something else.
something else.
But let's start by looking at what the input @italic{is}: Let's start by looking closely at what the input actually @italic{is}:
@i[ @i[
(define-syntax (show-me stx) (define-syntax (show-me stx)
@ -254,8 +253,11 @@ given:
(reverse-me "backwards" "am" "i" values) (reverse-me "backwards" "am" "i" values)
] ]
What's going on here? First we take the input syntax, and give it to Understand Yoda, can we. Great, but how does this work?
@racket[syntax->datum]. This converts the syntax into a plain old list:
First we take the input syntax, and give it to
@racket[syntax->datum]. This converts the syntax into a plain old
list:
@i[ @i[
(syntax->datum #'(reverse-me "backwards" "am" "i" values)) (syntax->datum #'(reverse-me "backwards" "am" "i" values))
@ -302,12 +304,12 @@ process of parsing, expanding and understanding your code. In other
words, your syntax transformer function is evaluated at compile time. words, your syntax transformer function is evaluated at compile time.
This aspect of macros lets you do things that simply aren't possible This aspect of macros lets you do things that simply aren't possible
in normal code. One of the classic examples, is something like the in normal code. One of the classic examples is something like the
Racket @racket[if] form: Racket form, @racket[if]:
@racket[(if <condition> <true-expression> <false-expression>)] @racket[(if <condition> <true-expression> <false-expression>)]
If @racket[if] were implemented as a function, all of the arguments If we implemented @racket[if] as a function, all of the arguments
would be evaluated before being provided to the function. would be evaluated before being provided to the function.
@i[ @i[
@ -538,8 +540,8 @@ shorthand.
@subsection{Patterns and templates} @subsection{Patterns and templates}
Most of the materials I found for learning macros, including the Most of the materials I found for learning macros, including the
Racket Guide, do a very good job explaining how the patterns work. I'm Racket @italic{Guide}, do a very good job explaining how patterns
not going to regurgitate that here. work. I'm not going to regurgitate that here.
Instead, let's look at some ways we're likely to get tripped up. Instead, let's look at some ways we're likely to get tripped up.
@ -547,14 +549,14 @@ Instead, let's look at some ways we're likely to get tripped up.
Let's say we want to define a function with a hyphenated name, a-b, Let's say we want to define a function with a hyphenated name, a-b,
but we supply the a and b parts separately. The Racket @racket[struct] but we supply the a and b parts separately. The Racket @racket[struct]
form does something like this---if we define a @racket[struct] named macro does something like this: @racket[(struct foo (field1 field2))]
@racket[foo], it defines a number of functions whose names are automatically defines a number of functions whose names are variations
variations on the name @racket[foo], such as @racket[foo-field1], on the name @racket[foo]---such as @racket[foo-field1],
@racket[foo-field2], @racket[foo?], and so on. @racket[foo-field2], @racket[foo?], and so on.
So let's pretend we're doing something like that. We want to So let's pretend we're doing something like that. We want to transform
transform the syntax @racket[(hyphen-define a b (args) body)] to the syntax @racket[(hyphen-define a b (args) body)] to the syntax
the syntax @racket[(define (a-b args) body)]. @racket[(define (a-b args) body)].
A wrong first attempt is: A wrong first attempt is:
@ -573,10 +575,11 @@ The "template" is the @racket[#'(define (name args ...) body0 body
sounds like we can't use @racket[a] (or @racket[b]) in the sounds like we can't use @racket[a] (or @racket[b]) in the
@racket[let] part. @racket[let] part.
It turns out that @racket[syntax-case] can have multiple Well, @racket[syntax-case] can have as many templates as you want. The
templates. The final expression is the obvious template, used to final expression is the obvious template, used to create the output
create the output syntax. But you can use @racket[syntax] a.k.a. #' on syntax. But you can use @racket[syntax] (a.k.a. #') on a pattern
a pattern variable, to make a template. Let's try that: variable. This makes another template, albeit a small, "fun size"
template. Let's try that:
@i[ @i[
(define-syntax (hyphen-define/wrong1.1 stx) (define-syntax (hyphen-define/wrong1.1 stx)
@ -589,35 +592,35 @@ a pattern variable, to make a template. Let's try that:
(foo-bar) (foo-bar)
] ]
Our macro definition didn't give an error, but when we tried to use Our macro definition didn't give an error, so that's good progress!
it, it didn't work. It seems that foo-bar didn't get defined. But when we tried to use it, no luck. It seems that a function named
@racket[foo-bar] wasn't defined.
This is where the Macro Stepper in DrRacket is invaluable. Even if you This is where the Macro Stepper in DrRacket is invaluable. Even if you
prefer to work in Emacs (like I do), in a situation like this it's prefer to work in Emacs (like I do), this is a situation where it's
worth firing up DrRacket temporarily to use the Macro Stepper. worth using DrRacket temporarily for its Macro Stepper.
It shows us: The Macro Stepper says that:
@codeblock{ @codeblock{
(module anonymous-module racket (hyphen-define/wrong1.1 foo bar () #t)
(#%module-begin
(define-syntax (hyphen-define/wrong1.1 stx)
(syntax-case stx ()
[(_ a b (args ...) body0 body ...)
(let ([name (string->symbol (format "~a-~a" #'a #'b))])
#'(define (name args ...) body0 body ...))]))
(define (name) #t)))
} }
It shows that we're expanding to @racket[(define (name) #t)], but we expanded to:
wanted to expand to @racket[(define (foo-bar) #t)].
@codeblock{
(define (name) #t)
}
We're expanding to @racket[(define (name) #t)], but we wanted to
expand to @racket[(define (foo-bar) #t)].
So the problem is we're getting @racket[name] when we wanted its So the problem is we're getting @racket[name] when we wanted its
value, @racket[foo-bar]. value, @racket[foo-bar].
The thing to reach for here is @racket[with-syntax]. This will say The thing to reach for here is @racket[with-syntax]. This will let us
that @racket[name] is in effect another pattern variable, the value of say that @racket[name] is in effect another pattern variable, the
which we want to use in our main output template. value of which we want to use in our main output template.
@i[ @i[
(define-syntax (hyphen-define/wrong1.3 stx) (define-syntax (hyphen-define/wrong1.3 stx)
@ -633,9 +636,12 @@ which we want to use in our main output template.
(foo-bar) (foo-bar)
] ]
Hmm. @racket[foo-bar] still not defined. Back to the Macro Stepper. It says we're expanding to: Hmm. @racket[foo-bar] is @italic{still} not defined. Back to the Macro
Stepper. It says now we're expanding to:
@racket[(define (|#<syntax:11:24foo>-#<syntax:11:28 bar>|) #t)]. @codeblock{
(define (|#<syntax:11:24foo>-#<syntax:11:28 bar>|) #t)
}
Ah, that's right. @racket[#'a] and @racket[#'b] are syntax Ah, that's right. @racket[#'a] and @racket[#'b] are syntax
objects. @racket[format] is printing a representation of them as syntax objects. @racket[format] is printing a representation of them as syntax