more docs
This commit is contained in:
parent
ebae6bd11d
commit
0571f4ebdb
|
@ -4,7 +4,7 @@
|
|||
@(require scribble/eval)
|
||||
|
||||
@(define my-eval (make-base-eval))
|
||||
@(my-eval `(require br))
|
||||
@(my-eval `(require br racket/stxparam))
|
||||
|
||||
|
||||
@title[#:style 'toc]{Beautiful Racket}
|
||||
|
@ -33,8 +33,8 @@ This library is designed to smooth over some of the small idiosyncrasies and inc
|
|||
|
||||
@defmodule[br/cond]
|
||||
|
||||
@defform[(while cond body ...)]
|
||||
Loop over @racket[_body] as long as @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out @racket[#f], @racket[_body] is never evaluated.
|
||||
@defform[(while cond body ...)]{
|
||||
Loop over @racket[body] as long as @racket[cond] is not @racket[#f]. If @racket[cond] starts out @racket[#f], @racket[body] is never evaluated.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(let ([x 42])
|
||||
|
@ -46,10 +46,10 @@ Loop over @racket[_body] as long as @racket[_cond] is not @racket[#f]. If @racke
|
|||
(unleash-zombie-army))
|
||||
x)
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@defform[(until cond body ...)]
|
||||
Loop over @racket[_body] until @racket[_cond] is not @racket[#f]. If @racket[_cond] starts out not @racket[#f], @racket[_body] is never evaluated.
|
||||
@defform[(until cond body ...)]{
|
||||
Loop over @racket[body] until @racket[cond] is not @racket[#f]. If @racket[cond] starts out not @racket[#f], @racket[body] is never evaluated.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(let ([x 42])
|
||||
|
@ -61,6 +61,7 @@ Loop over @racket[_body] until @racket[_cond] is not @racket[#f]. If @racket[_co
|
|||
(destroy-galaxy))
|
||||
x)
|
||||
]
|
||||
}
|
||||
|
||||
@section{Datums}
|
||||
|
||||
|
@ -76,8 +77,8 @@ When I use ``datum'' in its specific Racket sense, I use ``datums'' as its plura
|
|||
(format-datum
|
||||
[datum-form datum?]
|
||||
[val any/c?] ...)
|
||||
datum?]
|
||||
Similar to @racket[format], but the template @racket[_datum-form] is a datum, rather than a string, and the function returns a datum, rather than a string. Otherwise, the same formatting escapes can be used in the template (see @racket[fprintf]).
|
||||
datum?]{
|
||||
Similar to @racket[format], but the template @racket[datum-form] is a datum, rather than a string, and the function returns a datum, rather than a string. Otherwise, the same formatting escapes can be used in the template (see @racket[fprintf]).
|
||||
|
||||
Two special cases. First, a string that describes a list of datums is parenthesized so the result is a single datum. Second, an empty string returns @racket[void] (not @racket[#f], because that's a legitimate datum).
|
||||
|
||||
|
@ -89,13 +90,14 @@ Two special cases. First, a string that describes a list of datums is parenthesi
|
|||
(void? (format-datum '~a ""))
|
||||
(format-datum '~a #f)
|
||||
]
|
||||
}
|
||||
|
||||
@defproc[
|
||||
(format-datums
|
||||
[datum-form datum?]
|
||||
[vals (listof any/c?)] ...)
|
||||
(listof datum?)]
|
||||
Like @racket[format-datum], but applies @racket[_datum-form] to the lists of @racket[_vals] in similar way to @racket[map], where values for the format string are taken from the lists of @racket[_vals] in parallel. This means that a) @racket[_datum-form] must accept as many arguments as there are lists of @racket[_vals], and b) the lists of @racket[_vals] must all have the same number of items.
|
||||
(listof datum?)]{
|
||||
Like @racket[format-datum], but applies @racket[datum-form] to the lists of @racket[vals] in similar way to @racket[map], where values for the format string are taken from the lists of @racket[vals] in parallel. This means that a) @racket[datum-form] must accept as many arguments as there are lists of @racket[vals], and b) the lists of @racket[vals] must all have the same number of items.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(format-datums '~a '("foo" "bar" "zam"))
|
||||
|
@ -104,6 +106,7 @@ Like @racket[format-datum], but applies @racket[_datum-form] to the lists of @ra
|
|||
(format-datums '42 '("foo" "bar" "zam"))
|
||||
(format-datums '(~a ~a) '("foo" "bar" "zam") '(42))
|
||||
]
|
||||
}
|
||||
|
||||
@section{Debugging}
|
||||
|
||||
|
@ -113,8 +116,8 @@ Like @racket[format-datum], but applies @racket[_datum-form] to the lists of @ra
|
|||
@defform*[[
|
||||
(report expr)
|
||||
(report expr maybe-name)
|
||||
]]
|
||||
Print the name and value of @racket[_expr] to @racket[current-error-port], but also return the evaluated result of @racket[_expr] as usual. This lets you see the value of an expression or variable at runtime without disrupting any of the surrounding code. Optionally, you can use @racket[_maybe-name] to change the name shown in @racket[current-error-port].
|
||||
]]{
|
||||
Print the name and value of @racket[expr] to @racket[current-error-port], but also return the evaluated result of @racket[expr] as usual. This lets you see the value of an expression or variable at runtime without disrupting any of the surrounding code. Optionally, you can use @racket[maybe-name] to change the name shown in @racket[current-error-port].
|
||||
|
||||
For instance, suppose you wanted to see how @racket[first-condition?] was being evaluted in this expression:
|
||||
|
||||
|
@ -145,7 +148,7 @@ You can also add standalone calls to @racket[report] as a debugging aid at point
|
|||
@racketerror{x-before-function = 42
|
||||
@(linebreak)(first-condition? x) = #t}
|
||||
|
||||
But be careful — in the example below, the result of the @racket[if] expression will be skipped in favor of the last expression, which will be the value of @racket[_x]:
|
||||
But be careful — in the example below, the result of the @racket[if] expression will be skipped in favor of the last expression, which will be the value of @racket[x]:
|
||||
|
||||
@racketblock[
|
||||
(if (and (report (first-condition? x)) (second-condition? x))
|
||||
|
@ -155,12 +158,12 @@ But be careful — in the example below, the result of the @racket[if] expressi
|
|||
|
||||
|
||||
@defform[(report* expr ...)]
|
||||
Apply @racket[report] separately to each @racket[_expr] in the list.
|
||||
Apply @racket[report] separately to each @racket[expr] in the list.
|
||||
|
||||
|
||||
@defform*[((report-datum stx-expr) (report-datum stx-expr maybe-name))]
|
||||
A variant of @racket[report] for use with @secref["stx-obj" #:doc '(lib "scribblings/guide/guide.scrbl")]. Rather than print the whole object (as @racket[report] would), @racket[report-datum] prints only the datum inside the syntax object, but the return value is the whole syntax object.
|
||||
|
||||
}
|
||||
|
||||
@section{Define}
|
||||
|
||||
|
@ -170,9 +173,7 @@ A variant of @racket[report] for use with @secref["stx-obj" #:doc '(lib "scribbl
|
|||
(define-cases id
|
||||
[pat body ...+] ...+)
|
||||
]
|
||||
Define a function that behaves differently depending on how many arguments are supplied (also known as @seclink["Evaluation_Order_and_Arity" #:doc '(lib "scribblings/guide/guide.scrbl")]{@italic{arity}}). Like @racket[cond], you can have any number of branches. Each branch starts with a @racket[_pat] that accepts a certain number of arguments. If the current invocation of the function matches the number of arguments in @racket[_pat], then the @racket[_body] on the right-hand side is evaluated. If there is no matching case, an arity error arises.
|
||||
|
||||
Derived from @racket[case-lambda], which you might prefer.
|
||||
Define a function that behaves differently depending on how many arguments are supplied (also known as @seclink["Evaluation_Order_and_Arity" #:doc '(lib "scribblings/guide/guide.scrbl")]{@italic{arity}}). Like @racket[cond], you can have any number of branches. Each branch starts with a @racket[_pat] that accepts a certain number of arguments. If the current invocation of the function matches the number of arguments in @racket[_pat], then the @racket[_body] on the right-hand side is evaluated. If there is no matching case, an arity error arises. (Derived from @racket[case-lambda], whose notation you might prefer.)
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-cases f
|
||||
|
@ -202,6 +203,7 @@ Derived from @racket[case-lambda], which you might prefer.
|
|||
[
|
||||
(define-macro id (syntax other-id))
|
||||
(define-macro id (lambda (arg-id) result-expr ...+))
|
||||
(define-macro id transformer-id)
|
||||
(define-macro id (syntax result-expr))
|
||||
(define-macro (id pat-arg ...) expr ...+)
|
||||
]]
|
||||
|
@ -209,9 +211,9 @@ Create a macro using one of the subforms above, which are explained below:
|
|||
|
||||
@specsubform[#:literals (define-macro syntax lambda stx)
|
||||
(define-macro id (syntax other-id))]{
|
||||
If the first argument is an identifier @racket[_id] and the second a syntaxed identifier that looks like @racket[(syntax other-id)], create a @tech{rename transformer}, which is a fancy term for ``macro that replaces @racket[_id] with @racket[_other-id].'' (This subform is equivalent to @racket[make-rename-transformer].)
|
||||
If the first argument is an identifier @racket[id] and the second a syntaxed identifier that looks like @racket[(syntax other-id)], create a @tech{rename transformer}, which is a fancy term for ``macro that replaces @racket[id] with @racket[other-id].'' (This subform is equivalent to @racket[make-rename-transformer].)
|
||||
|
||||
Why do we need rename transformers? Because an ordinary macro operates on its whole calling expression, like @racket[(macro-name this-arg that-arg . and-so-on)]. Whereas a rename transformer operates only on the identifier itself (regardless of where it appears in the code). It's like creating an alias for an identifier.
|
||||
Why do we need rename transformers? Because an ordinary macro operates on its whole calling expression (which it receives as input) like @racket[(macro-name this-arg that-arg . and-so-on)]. By contrast, a rename transformer operates only on the identifier itself (regardless of where that identifier appears in the code). It's like making one identifier into an alias for another identifier.
|
||||
|
||||
Below, notice how the rename transformer, operating in the macro realm, approximates the behavior of a run-time assignment.
|
||||
|
||||
|
@ -231,8 +233,7 @@ zam-macro
|
|||
|
||||
@specsubform[#:literals (define-macro lambda stx)
|
||||
(define-macro id (lambda (arg-id) result-expr ...+))]{
|
||||
If the first argument is an @racket[_id] and the second a single-argument procedure, create a macro called @racket[_id] that uses the procedure as a @tech{syntax transformer}. This transformer must return a @tech{syntax object}, otherwise you'll trigger an error. Beyond that, the transformer can do whatever you like. (This subform is equivalent to @racket[define-syntax].)
|
||||
|
||||
If the first argument is an @racket[id] and the second a single-argument function, create a macro called @racket[id] that uses the function as a syntax transformer. This function must return a @seclink["stx-obj" #:doc '(lib "scribblings/guide/guide.scrbl")]{syntax object}, otherwise you'll trigger an error. Beyond that, the function can do whatever you like. (This subform is equivalent to @racket[define-syntax].)
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro nice-sum (lambda (stx) #'(+ 2 2)))
|
||||
|
@ -240,16 +241,28 @@ nice-sum
|
|||
(define-macro not-nice (lambda (stx) '(+ 2 2)))
|
||||
not-nice
|
||||
]
|
||||
}
|
||||
|
||||
@specsubform[#:literals (define-macro lambda stx)
|
||||
(define-macro id transformer-id)]{
|
||||
Similar to the previous subform, but @racket[transformer-id] holds an existing transformer function. Note that @racket[transformer-id] needs to be visible during compile time (aka @italic{phase 1}), so use @racket[define-for-syntax] or equivalent.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-for-syntax summer-compile-time (lambda (stx) #'(+ 2 2)))
|
||||
(define-macro nice-summer summer-compile-time)
|
||||
nice-summer
|
||||
(define summer-run-time (lambda (stx) #'(+ 2 2)))
|
||||
(define-macro not-nice-summer summer-run-time)
|
||||
]
|
||||
}
|
||||
|
||||
@specsubform[#:literals (define-macro)
|
||||
(define-macro id syntax-object)
|
||||
#:contracts ([syntax-object syntax?])]{
|
||||
If the first argument is an @racket[_id] and the second a @racket[_syntax-object], create a @tech{syntax transformer} that returns @racket[_syntax-object]. This is just alternate notation for the previous subform, wrapping @racket[_syntax-object] inside a syntax-transformer body. The effect is to create a macro from @racket[_id] that always returns @racket[_syntax-object], regardless of how it's invoked. Not especially useful within programs. Mostly handy for making quick macros at the @tech{REPL}.
|
||||
If the first argument is an @racket[id] and the second a @racket[syntax-object], create a syntax transformer that returns @racket[syntax-object]. This is just alternate notation for the previous subform, wrapping @racket[syntax-object] inside a function body. The effect is to create a macro from @racket[id] that always returns @racket[syntax-object], regardless of how it's invoked. Not especially useful within programs. Mostly handy for making quick macros at the @tech{REPL}.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro bad-listener #'"did you say something?")
|
||||
(define-macro bad-listener #'"what?")
|
||||
bad-listener
|
||||
(bad-listener)
|
||||
(bad-listener "hello")
|
||||
|
@ -260,9 +273,10 @@ bad-listener
|
|||
|
||||
@specsubform[#:literals (define-macro)
|
||||
(define-macro (id pat-arg ...) result-expr ...+)]{
|
||||
If the first argument is a @tech{syntax pattern} starting with @racket[_id], then create a @tech{syntax transformer} for this pattern using @racket[_result-expr ...] as the return value. As usual, @racket[_result-expr ...] needs to return a @tech{syntax object} or you'll get an error.
|
||||
If the first argument is a @seclink["stx-patterns" #:doc '(lib "scribblings/reference/reference.scrbl")]
|
||||
{syntax pattern} starting with @racket[id], then create a syntax transformer for this pattern using @racket[result-expr ...] as the return value. As usual, @racket[result-expr ...] needs to return a @seclink["stx-obj" #:doc '(lib "scribblings/guide/guide.scrbl")]{syntax object} or you'll get an error.
|
||||
|
||||
By convention, if a @racket[pat-arg] has a @tt{CAPITALIZED-NAME}, it's treated as a named wildcard match (meaning, it will match any expression in that position, and can be subsequently referred to by that name). Otherwise, @racket[pat-arg] is treated as a literal matcher (meaning, it will only match the same expression).
|
||||
The syntax-pattern notation is the same as @racket[syntax-case], with one key difference. If a @racket[pat-arg] has a @tt{CAPITALIZED-NAME}, it's treated as a named wildcard (meaning, it will match any expression in that position, and can be subsequently referred to by that name). Otherwise, @racket[pat-arg] is treated as a literal (meaning, it will only match the same expression).
|
||||
|
||||
For instance, the @racket[sandwich] macro below requires three arguments, and the third must be @racket[please], but the other two are wildcards:
|
||||
|
||||
|
@ -277,7 +291,7 @@ For instance, the @racket[sandwich] macro below requires three arguments, and th
|
|||
|
||||
]
|
||||
|
||||
The ellipsis @racket[...] can be used with a wildcard matcher to match a list of arguments. Please note: though a wildcard matcher standing alone must match one argument, once you add an ellipsis, it's allowed to match zero:
|
||||
The ellipsis @racket[...] can be used with a wildcard to match a list of arguments. Please note: though a wildcard standing alone must match one argument, once you add an ellipsis, it's allowed to match zero:
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro (pizza TOPPING ...)
|
||||
|
@ -290,7 +304,7 @@ The ellipsis @racket[...] can be used with a wildcard matcher to match a list of
|
|||
(pizza)
|
||||
]
|
||||
|
||||
The capitalization convention for a wildcard @racket[pat-arg] makes it easy to mix literal and wildcard matchers in one pattern. But it also makes it easy to mistype a pattern and not get the wildcard matcher you were expecting. Below, @racket[bad-squarer] doesn't work because @racket[any-number] is meant to be a wildcard. But it's not capitalized, so it's considered a literal matcher, and it triggers an error:
|
||||
The capitalization requirement for a wildcard @racket[pat-arg] makes it easy to mix literals and wildcards in one pattern. But it also makes it easy to mistype a pattern and not get the wildcard you were expecting. Below, @racket[bad-squarer] doesn't work because @racket[any-number] is meant to be a wildcard. But it's not capitalized, so it's considered a literal, and it triggers an error:
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro (bad-squarer any-number)
|
||||
|
@ -298,7 +312,7 @@ The capitalization convention for a wildcard @racket[pat-arg] makes it easy to m
|
|||
(bad-squarer +10i)
|
||||
]
|
||||
|
||||
The error is cleared when the argument is capitalized, thus making it a wilcard matcher:
|
||||
The error is cleared when the argument is capitalized, thus making it a wilcard:
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro (good-squarer ANY-NUMBER)
|
||||
|
@ -306,21 +320,92 @@ The error is cleared when the argument is capitalized, thus making it a wilcard
|
|||
(good-squarer +10i)
|
||||
]
|
||||
|
||||
You can use the special identifier @racket[caller-stx] — available only within the body of @racket[define-macro] — to access the original input argument to the macro.
|
||||
|
||||
@;{todo: fix this example. complains that caller-stx is unbound}
|
||||
@;{
|
||||
@examples[#:eval my-eval
|
||||
(require (for-syntax br))
|
||||
(define-macro (inspect ARG ...)
|
||||
#`(displayln
|
||||
(let ([calling-pattern '#,(syntax->datum caller-stx)])
|
||||
(format "Called as ~a with ~a args"
|
||||
calling-pattern
|
||||
(length (cdr calling-pattern))))))
|
||||
|
||||
(inspect)
|
||||
(inspect 42)
|
||||
(inspect "foo" "bar")
|
||||
(inspect #t #f #f #t)
|
||||
]
|
||||
}
|
||||
|
||||
This subform of @racket[define-macro] is useful for macros that have one calling pattern. To make a macro with multiple calling patterns, see @racket[define-macro-cases].
|
||||
}
|
||||
|
||||
|
||||
@defform*[
|
||||
[
|
||||
(define-macro-cases id [(_ pat-arg ...) result-expr ...+] ...+)
|
||||
]]
|
||||
TBD
|
||||
@defform[
|
||||
(define-macro-cases id
|
||||
[pattern result-expr ...+] ...+)
|
||||
]{
|
||||
Create a macro called @racket[id] with multiple branches, each with a @racket[pattern] on the left and @racket[result-expr] on the right. The input to the macro is tested against each @racket[pattern]. If it matches, then @racket[result-expr] is evaluated.
|
||||
|
||||
As with @racket[define-macro], wildcards in each syntax pattern must be @tt{CAPITALIZED}. Everything else is treated as a literal match, except for the ellipsis @racket[...] and the wildcard @racket[_].
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define-macro-cases yogurt
|
||||
[(yogurt) #'(displayln (format "No toppings? Really?"))]
|
||||
[(yogurt TOPPING)
|
||||
#'(displayln (format "Sure, you can have ~a." 'TOPPING))]
|
||||
[(yogurt TOPPING ANOTHER-TOPPING ... please)
|
||||
#'(displayln (format "Since you asked nicely, you can have ~a toppings."
|
||||
(length '(TOPPING ANOTHER-TOPPING ...))))]
|
||||
[(yogurt TOPPING ANOTHER-TOPPING ...)
|
||||
#'(displayln (format "Whoa! Rude people only get one topping."))])
|
||||
|
||||
(yogurt)
|
||||
(yogurt granola)
|
||||
(yogurt coconut almonds hot-fudge brownie-bites please)
|
||||
(yogurt coconut almonds)
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@section{Reader utilities}
|
||||
|
||||
@defmodule[br/reader-utils]
|
||||
|
||||
TK
|
||||
@defform[
|
||||
(define-read-and-read-syntax (path-id port-id)
|
||||
reader-result-expr ...+)
|
||||
]{
|
||||
For use within a language reader. Automatically @racket[define] and @racket[provide] the @racket[read] and @racket[read-syntax] functions needed for the reader's public interface. @racket[reader-result-expr] can return either a syntax object or a datum (which will be converted to a syntax object).
|
||||
|
||||
The generated @racket[read-syntax] function takes two arguments, a path and an input port. It returns a syntax object stripped of all bindings.
|
||||
|
||||
The generated @racket[read] function takes one argument, an input port. It calls @racket[read-syntax] and converts the result to a datum.
|
||||
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(module sample-reader racket/base
|
||||
(require br/reader-utils racket/list)
|
||||
(define-read-and-read-syntax (path port)
|
||||
(add-between
|
||||
(for/list ([datum (in-port read port)])
|
||||
datum)
|
||||
'whee)))
|
||||
|
||||
(require (prefix-in sample: 'sample-reader))
|
||||
|
||||
(define string-port (open-input-string "(+ 2 2) 'hello"))
|
||||
(sample:read-syntax 'no-path string-port)
|
||||
|
||||
(define string-port-2 (open-input-string "(+ 2 2) 'hello"))
|
||||
(sample:read string-port-2)
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
|
||||
@section{Syntax}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user