Syntax-parse
This commit is contained in:
parent
d82e4b7911
commit
c42a25ed78
151
main.rkt
151
main.rkt
|
@ -14,6 +14,11 @@
|
||||||
[sandbox-error-output 'string])
|
[sandbox-error-output 'string])
|
||||||
(make-evaluator 'racket)))
|
(make-evaluator 'racket)))
|
||||||
|
|
||||||
|
@(define typed/evaluator
|
||||||
|
(parameterize ([sandbox-output 'string]
|
||||||
|
[sandbox-error-output 'string])
|
||||||
|
(make-evaluator 'typed/racket)))
|
||||||
|
|
||||||
@(define-syntax-rule (i body ...)
|
@(define-syntax-rule (i body ...)
|
||||||
(interaction #:eval evaluator body ...))
|
(interaction #:eval evaluator body ...))
|
||||||
|
|
||||||
|
@ -1046,7 +1051,7 @@ Later, we'll see how @racket[syntax-parse] makes it even easier to
|
||||||
check usage and provide helpful messages about mistakes.
|
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
|
The previous two examples used a macro to define functions whose names
|
||||||
were made by joining identifiers provided to the macro. This example
|
were made by joining identifiers provided to the macro. This example
|
||||||
|
@ -1228,7 +1233,7 @@ conflict with a variable named @racket[x] in an outer scope:
|
||||||
(printf "The outer `x' is ~s\n" x))
|
(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.
|
reliable macros that behave predictably.
|
||||||
|
|
||||||
So that's wonderful default behavior. But @italic{sometimes} we want
|
So that's wonderful default behavior. But @italic{sometimes} we want
|
||||||
|
@ -1286,18 +1291,6 @@ But we can still define @racket[it] as a normal variable:
|
||||||
it
|
it
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------------
|
|
||||||
@; ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@section{Robust macros: syntax-parse}
|
|
||||||
|
|
||||||
TO-DO.
|
|
||||||
|
|
||||||
TO-DO.
|
|
||||||
|
|
||||||
TO-DO.
|
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------------
|
@; ----------------------------------------------------------------------------
|
||||||
@; ----------------------------------------------------------------------------
|
@; ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1370,12 +1363,140 @@ The splicing variation is more convenient than the usual way:
|
||||||
x))))
|
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.
|
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}
|
@section{References and Acknowledgments}
|
||||||
|
|
||||||
Eli Barzliay's blog post,
|
Eli Barzliay's blog post,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user