Various edits for prose and layouts.
This commit is contained in:
parent
65e3eca65e
commit
d554435088
63
index.html
63
index.html
File diff suppressed because one or more lines are too long
94
main.rkt
94
main.rkt
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user