Merge branch 'master' into gh-pages

This commit is contained in:
Greg Hendershott 2012-11-02 11:37:31 -04:00
commit 8246a2fc7f
3 changed files with 304 additions and 122 deletions

24
gh.css
View File

@ -4,16 +4,28 @@
text-decoration: line-through;
}
/*** racket.css ***/
/**********************************************************************/
/* racket.css */
/* Monospace: */
.RktIn, .RktRdr, .RktPn, .RktMeta,
.RktMod, .RktKw, .RktVar, .RktSym,
.RktRes, .RktOut, .RktCmt, .RktVal, .hspace {
font-family: monospace;
font-size: 95%;
white-space: inherit;
}
.RktValLink, .RktModLink {
color: #2626c0; /* NOT bright blue */
}
.RktErr {
color: #c02626; /* NOT bright red */
font-family: monospace;
font-size: 95%;
}
/* Serif: */
.inheritedlbl {
font-family: 'Fenix',serif;
@ -24,14 +36,8 @@
font-family: sans-serif;
}
.RktBlk {
background-color: #f8faf8;
white-space: inherit;
text-align: left;
}
/*** scribble.css ***/
/**********************************************************************/
/* scribble.css */
/* Monospace: */
.maincolumn, .refpara, .refelem, .tocset, .stt, .refparaleft, .refelemleft {

File diff suppressed because one or more lines are too long

296
main.rkt
View File

@ -14,6 +14,11 @@
[sandbox-error-output 'string])
(make-evaluator 'racket)))
@(define typed/evaluator
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string])
(make-evaluator 'typed/racket)))
@(define-syntax-rule (i body ...)
(interaction #:eval evaluator body ...))
@ -79,7 +84,7 @@ understand the answers and solutions.
@; ----------------------------------------------------------------------------
@section{The plan of attack}
@section{Our plan of attack}
The macro system you will mostly want to use for production-quality
macros is called @racket[syntax-parse]. And don't worry, we'll get to
@ -89,10 +94,10 @@ But if we start there, you're likely to feel overwhelmed by concepts
and terminology, and get very confused. I did.
1. Instead let's start with the basics: A syntax object and a function
to change it (a "transformer"). We'll work at that level for awhile to
to change it---a "transformer". We'll work at that level for awhile to
get comfortable and to de-mythologize this whole macro business.
2. Next, we'll realize that some pattern-matching would make life
2. Soon we'll realize that pattern-matching would make life
easier. We'll learn about @racket[syntax-case] and its shorthand
cousin, @racket[define-syntax-rule]. We'll discover we can get
confused if we want to munge pattern variables before sticking them
@ -117,7 +122,7 @@ enhancements is @racket[syntax-parse].
@; ----------------------------------------------------------------------------
@; ----------------------------------------------------------------------------
@section{Transformers}
@section{Transform!}
@verbatim[#:indent 2]{
YOU ARE INSIDE A ROOM.
@ -227,9 +232,9 @@ which is used to evaluate and run our program.
@subsection{What's the input?}
Our examples so far ignored the input syntax, and output a fixed
syntax. But instead of throwing away the input, usually we want to
transform the input.
Our examples so far have ignored the input syntax and output some
fixed syntax. But typically we will want to transform in the input
syntax into somehing else.
Let's start by looking closely at what the input actually @italic{is}:
@ -244,7 +249,7 @@ The @racket[(print stx)] shows what our transformer is given: a syntax
object.
A syntax object consists of several things. The first part is the
s-expression representing the code, such as @racket['(+ 1 2)].
S-expression representing the code, such as @racket['(+ 1 2)].
Racket syntax is also decorated with some interesting information such
as the source file, line number, and column. Finally, it has
@ -260,7 +265,7 @@ stx
]
Now let's use functions that access the syntax object. The source
information functions:
information functions are:
@margin-note{@racket[(syntax-source stx)] is returning @racket['eval],
only becaue of how I'm generating this documentation, using an
@ -274,7 +279,7 @@ somthing like "my-file.rkt".}
]
More interesting is the syntax "stuff" itself. @racket[syntax->datum]
converts it completely into an s-expression:
converts it completely into an S-expression:
@i[
(syntax->datum stx)
@ -297,9 +302,8 @@ In most cases, @racket[syntax->list] gives the same result as
(syntax->list stx)
]
When would @racket[syntax-e] and @racket[syntax->list] differ? Let's
not get side-tracked now.
(When would @racket[syntax-e] and @racket[syntax->list] differ? Let's
not get side-tracked now.)
When we want to transform syntax, we'll generally take the pieces we
were given, maybe rearrange their order, perhaps change some of the
@ -356,9 +360,9 @@ compiler, and @italic{that} syntax is evaluated:
@subsection{Compile time vs. run time}
@codeblock[#:indent 10]{
@codeblock0{
(define-syntax (foo stx)
(make-pipe) ;This is not run time.
(make-pipe) ;Ce n'est pas le temps d'exécution
#'(void))
}
@ -520,6 +524,8 @@ So let's try that:
(our-if-using-match-v2 #t "true" "false")
]
Joy.
@; ----------------------------------------------------------------------------
@subsection{@racket[begin-for-syntax]}
@ -569,7 +575,7 @@ syntax without evaluating them. We can implement forms like
@item{More good news is that there isn't some special, weird language
for writing syntax transformers. We can write these transformer
functions using the Racket language we already know and lovex.}
functions using the Racket language we already know and love.}
@item{The semi-bad news is that the familiarity can make it easy to forget
that we're not working at run time. Sometimes that's important to
@ -671,7 +677,7 @@ working.
@; ----------------------------------------------------------------------------
@subsection{"A pattern variable can't be used outside of a template"}
@subsection{Pattern variable vs. template---fight!}
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]
@ -754,20 +760,21 @@ Well that explains it. Instead, we wanted to expand to:
Our template is using the symbol @racket[name] but we wanted its
value, such as @racket[foo-bar] in this use of our macro.
Can we think of something we already know that behaves like
this---where using a variable in the template yields its value? Sure
we do: Pattern variables. Our pattern doesn't include @racket[name]
because we don't expect it in the original syntax---indeed the whole
point of this macro is to create it. So @racket[name] can't be in the
main pattern. Fine---let's make an @italic{additional} pattern. We can
do that using an additional, nested @racket[syntax-case]:
Is there anything we already know that behaves like this---where using
a variable in the template yields its value? Yes: Pattern
variables. Our pattern doesn't include @racket[name] because we don't
expect it in the original syntax---indeed the whole point of this
macro is to create it. So @racket[name] can't be in the main
pattern. Fine---let's make an @italic{additional} pattern. We can do
that using an additional, nested @racket[syntax-case]:
@i[
(define-syntax (hyphen-define/wrong1.2 stx)
(syntax-case stx ()
[(_ a b (args ...) body0 body ...)
(syntax-case (datum->syntax stx
(string->symbol (format "~a-~a" #'a #'b))) ()
(string->symbol (format "~a-~a" #'a #'b)))
()
[name #'(define (name args ...)
body0 body ...)])]))
]
@ -793,23 +800,19 @@ Let's try to use our new version:
Hmm. @racket[foo-bar] is @italic{still} not defined. Back to the Macro
Stepper. It says now we're expanding to:
@racketblock[
(define (|#<syntax:11:24foo>-#<syntax:11:28 bar>|) #t)
]
@racketblock[(define (|#<syntax:11:24foo>-#<syntax:11:28 bar>|) #t)]
Oh right: @racket[#'a] and @racket[#'b] are syntax objects. Therefore
@racketblock[(string->symbol (format "~a-~a" #'a #'b))]
is something like
is the printed form of both syntax objects, joined by a hyphen:
@racketblock[|#<syntax:11:24foo>-#<syntax:11:28 bar>|]
---the printed form of both syntax objects, joined by a hyphen.
Instead we want the datum in the syntax objects (such as the symbols
@racket[foo] and @racket[bar]). Let's use @racket[syntax->datum] to
get it:
Instead we want the datum in the syntax objects, such as the symbols
@racket[foo] and @racket[bar]. Which we get using
@racket[syntax->datum]:
@i[
(define-syntax (hyphen-define/ok1 stx)
@ -818,7 +821,8 @@ get it:
(syntax-case (datum->syntax stx
(string->symbol (format "~a-~a"
(syntax->datum #'a)
(syntax->datum #'b)))) ()
(syntax->datum #'b))))
()
[name #'(define (name args ...)
body0 body ...)])]))
(hyphen-define/ok1 foo bar () #t)
@ -829,7 +833,7 @@ And now it works!
Now for two shortcuts.
Instead of an additional, nested @racket[syntax-case] we could use
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
rearranges the @racket[syntax-case] to look more like a @racket[let]
@ -901,7 +905,7 @@ To review:
@item{You can't use a pattern variable outside of a template. But
you can use @racket[syntax] or @tt{#'} on a pattern variable to make
an ad hoc "fun size" template.}
an ad hoc, "fun size" template.}
@item{If you want to munge pattern variables for use in the
template, @racket[with-syntax] is your friend, because it lets you
@ -1047,7 +1051,7 @@ Later, we'll see how @racket[syntax-parse] makes it even easier to
check usage and provide helpful messages about mistakes.
@subsection{Using dot notation for nested hash lookups}
@subsection[#:tag "hash.refs"]{Using dot notation for nested hash lookups}
The previous two examples used a macro to define functions whose names
were made by joining identifiers provided to the macro. This example
@ -1057,9 +1061,15 @@ pieces.
If you write programs for web services you deal with JSON, which is
represented in Racket by a @racket[jsexpr?]. JSON often has
dictionaries that contain other dictionaries. In a @racket[jsexpr?]
these are represented by nested @racket[hasheq] tables.
these are represented by nested @racket[hasheq] tables:
JavaScript you can use dot notation:
@#reader scribble/comment-reader
(i
; Nested `hasheq's typical of a jsexpr:
(define js (hasheq 'a (hasheq 'b (hasheq 'c "value"))))
)
In JavaScript you can use dot notation:
@codeblock{
foo = js.a.b.c;
@ -1067,13 +1077,7 @@ foo = js.a.b.c;
In Racket it's not so convenient:
@#reader scribble/comment-reader
(i
; Nested hasheqs typical of a jsexpr:
(define js (hasheq 'a (hasheq 'b (hasheq 'c "value"))))
; Typical annoying code to get something:
(hash-ref (hash-ref (hash-ref js 'a) 'b) 'c)
)
@racketblock[(hash-ref (hash-ref (hash-ref js 'a) 'b) 'c)]
We can write a helper function to make this a bit cleaner:
@ -1092,7 +1096,7 @@ We can write a helper function to make this a bit cleaner:
(hash-refs js '(a b c))
)
That's not bad. Can we go even further and use a dot notation somewhat
That's better. Can we go even further and use a dot notation somewhat
like JavaScript?
@#reader scribble/comment-reader
@ -1101,12 +1105,43 @@ like JavaScript?
(require (for-syntax racket/syntax))
(define-syntax (hash.refs stx)
(syntax-case stx ()
;; If the optional `default' is missing, assume it's #f.
[(_ chain)
#'(hash.refs chain #f)]
[(_ chain default)
(let ([xs (map (lambda (x)
(datum->syntax stx (string->symbol x)))
(regexp-split #rx"\\."
(symbol->string (syntax->datum #'chain))))])
(with-syntax ([h (car xs)]
[ks (cdr xs)])
#'(hash-refs h 'ks default)))]))
;; Gives us "sugar" to say this:
(hash.refs js.a.b.c)
;; Try finding a key that doesn't exist:
(hash.refs js.blah)
;; Try finding a key that doesn't exist, specifying the default:
(hash.refs js.blah 'did-not-exist)
)
It works!
We've started to appreciate that our macros should give helpful
messages when used in error. Let's try to do that here.
@#reader scribble/comment-reader
(i
(require (for-syntax racket/syntax))
(define-syntax (hash.refs stx)
(syntax-case stx ()
;; Check for no args at all
[(_)
(raise-syntax-error #f "Expected (hash.key0[.key1 ...] [default])"
stx #'chain)]
[(_ chain)
#'(hash.refs chain #f)]
[(_ chain default)
;; Check that chain is a symbol, not e.g. a number or string
(unless (symbol? (syntax-e #'chain))
(raise-syntax-error #f "Expected (hash.key0[.key1 ...] [default])"
stx #'chain))
@ -1114,30 +1149,25 @@ like JavaScript?
(datum->syntax stx (string->symbol x)))
(regexp-split #rx"\\."
(symbol->string (syntax->datum #'chain))))])
;; Check that we have at least hash.key
(unless (and (>= (length xs) 2)
(not (eq? (syntax-e (cadr xs)) '||)))
(raise-syntax-error #f "Expected hash.key" stx #'chain))
(with-syntax ([h (car xs)]
[ks (cdr xs)])
#'(hash-refs h 'ks default)))]))
;; Gives us "sugar" to say this:
(hash.refs js.a.b.c)
)
It works!
We've started to appreciate that our macros should give helpful
messages when used in error. We tried to do that here. Let's
deliberately elicit various errors. Are the messages helpful?
@i[
;; See if we catch each of the misuses
(hash.refs)
(hash.refs 0)
(hash.refs js)
(hash.refs js.)
]
)
Not too bad.
Not too bad. Of course, the version with error-checking is quite a bit
longer. Error-checking code generally tends to obscure the logic, and
does here. Fortuantely we'll soon see how @racket[syntax-parse] can
help mitigate that, in much the same way as contracts in normal
Racket or types in Typed Racket.
Maybe we're not convinced that writing @racket[(hash.refs js.a.b.c)]
is really clearer than @racket[(hash-refs js '(a b c))]. Maybe we
@ -1203,7 +1233,7 @@ conflict with a variable named @racket[x] in an outer scope:
(printf "The outer `x' is ~s\n" x))
]
When your macros also respect lexical scoping, it's easy to write
When our macros also respect lexical scoping, it's easier to write
reliable macros that behave predictably.
So that's wonderful default behavior. But @italic{sometimes} we want
@ -1261,18 +1291,6 @@ But we can still define @racket[it] as a normal variable:
it
]
@; ----------------------------------------------------------------------------
@; ----------------------------------------------------------------------------
@section{Robust macros: syntax-parse}
TO-DO.
TO-DO.
TO-DO.
@; ----------------------------------------------------------------------------
@; ----------------------------------------------------------------------------
@ -1345,12 +1363,140 @@ The splicing variation is more convenient than the usual way:
x))))
)
When there are many body forms---and you are generating them in a
When there are many body forms---and we're generating them in a
macro---the splicing variations can be much easier.
@; ----------------------------------------------------------------------------
@; ----------------------------------------------------------------------------
@section{Robust macros: syntax-parse}
Functions can be used in error. So can macros.
@subsection{Error-handling strategies for functions}
With plain old functions, we have several choices.
1. Don't check at all.
@#reader scribble/comment-reader
(i
(define (misuse s)
(string-append s " snazzy suffix"))
;; User of the function:
(misuse 0)
;; I guess I goofed, but -- what is this "string-append" of which you
;; speak??
)
The problem is that the resulting error message will be confusing. Our
user thinks they're calling @racket[misuse], but is getting an error
message from @racket[string-append]. In this simple example they
could probably guess what's happening, but in most cases they won't.
2. Write some error handling code.
@#reader scribble/comment-reader
(i
(define (misuse s)
(unless (string? s)
(error 'misuse "expected a string, but got ~a" s))
(string-append s " snazzy suffix"))
;; User of the function:
(misuse 0)
;; I goofed, and understand why! It's a shame the writer of the
;; function had to work so hard to tell me.
)
Unfortunately the error code tends to overwhelm and/or obscure our
function definition. Also, the error message is good but not
great. Improving it would require even more error code.
3. Use a contract.
@#reader scribble/comment-reader
(i
(define/contract (misuse s)
(string? . -> . string?)
(string-append s " snazzy suffix"))
;; User of the function:
(misuse 0)
;; I goofed, and understand why! I hear the writer of the function is
;; happier.
)
This is the best of both worlds.
The contract is a simple and concise. Even better, it's
declarative. We say what we want, without needing to spell out what to
do.
On the other hand the user of our function gets a very detailed error
message. Plus, the message is in a standard, familiar format.
4. Use Typed Racket.
@codeblock{#lang typed/racket}
@interaction[#:eval typed/evaluator
(: misuse (String -> String))
(define (misuse s)
(string-append s " snazzy suffix"))
;; User of the function:
(misuse 0)
;; I goofed, and understand why! I hear the writer of the function is
;; a cheerful type.
]
With respect to error handling, Typed Racket has the same benefits as
contracts. Good.
@subsection{Error-handling strategies for macros}
For macros, we have similar choices.
1. Ignore the possibility of misuse. This choice is even worse for
macros. The default error messages are even less likely to make sense,
much less help our user know what to do.
2. Write error-handling code. We saw how much this complicated our
macros in our example of @secref["hash.refs"]. And while we're still
learning how to write macros, we especially don't want more cognitive
load and obfuscation.
3. Use @racket[syntax/parse]. For macros, this is the equivalent of
using contracts or types for functions. We can declare that input
pattern elements must be certain kinds of things, such as an
identifier. Instead of "types", the things are referred to as "syntax
classes". There are predefined syntax classes, plus we can define our own.
@subsection{Using @racket[syntax/parse]}
November 1, 2012: So here's the deal. After writing everything up to
this point, I sat down to re-read the documentation for
@racket[syntax/parse]. It was...very understandable. I didn't feel
confused.
@codeblock{
<span style='accent: "Kenau-Reeves"'>
Whoa.
</span>
}
Why? The documentation is written very well. Also, everything up to
this point prepared me to appreciate what @racket[syntax/parse] does,
and why. That leaves the "how" of using it, which seems pretty
straightforward, so far.
This might well be a temporary state of me "not knowing what I don't
know". As I dig in and use it more, maybe I'll discover something
confusing or tricky. If/when I do, I'll come back here and update
this.
But for now I'll focus on improving the previous parts.
@; ----------------------------------------------------------------------------
@; ----------------------------------------------------------------------------
@section{References and Acknowledgments}
Eli Barzliay's blog post,