www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

examples.scrbl (11474B)


      1 #lang scribble/manual
      2 
      3 @(require racket/require
      4           scribble/example
      5           "orig.rkt"
      6           (for-label subtemplate
      7                      (only-in syntax/parse/experimental/template)
      8                      (subtract-in racket/base subtemplate)))
      9 
     10 @title{Examples}
     11 
     12 This section contains a few (somewhat artificial) examples on how to use
     13 @racketmodname[subtemplate]. Most of the examples here would be more wisely
     14 written as functions, and @racketmodname[syntax/parse] would otherwise be
     15 sufficient with slightly more verbose code. The tools offered by
     16 @racketmodname[subtemplate] are more useful for complex macros, where
     17 boilerplate should be elided as much as possible to leave the true structure
     18 of the macro visible.
     19 
     20 @section{Automatically deriving identifiers using subscripts}
     21 
     22 When an identifier @racket[yᵢ] is encountered in a template, it is
     23 automatically derived from the corresponding @racket[xᵢ]. In the following
     24 example, @racket[tempᵢ] is implicitly bound to
     25 @racket[#'(a/temp b/temp c/temp)], without the need to call
     26 @racket[generate-temporaries].
     27 
     28 @examples[
     29  (require subtemplate/override)
     30  (syntax-parse #'(a b c)
     31    [(vᵢ …)
     32     #'([tempᵢ vᵢ] …)])]
     33 
     34 It is common in macros to save an expression in a temporary variable to avoid
     35 executing it twice. The following example builds on the previous one to do so,
     36 without the need to call @racket[generate-temporaries]. Note that the
     37 temporary identifiers generated this way are hygienic: there will be no name
     38 clashes with identifiers from the user, nor with identifiers directly created
     39 by your macro.
     40 
     41 @examples[
     42  (require racket/require
     43           (for-syntax (subtract-in racket/base subtemplate/override)
     44                       subtemplate/override))
     45  (define-syntax sum
     46    (syntax-parser
     47      [(_ vᵢ …)
     48       #'(let ([tempᵢ vᵢ] …)
     49           (unless (integer? tempᵢ)
     50             (printf "Warning: ~a should be an integer, got ~a.\n"
     51                     'vᵢ
     52                     tempᵢ))
     53     54           (+ tempᵢ …))]))
     55  (sum 1 2 3)
     56  (sum 1 (begin (displayln "executed once") 2) 3)
     57  (sum 1 (+ 3 0.14) 3)]
     58 
     59 If you run out of unicode subscripts, characters following the last @racket[_]
     60 are treated as the subscript:
     61 
     62 @examples[
     63  (require subtemplate/override)
     64  (syntax-parse #'(a b c)
     65    [(v_foo …)
     66     #'([temp_foo v_foo] …)])]
     67 
     68 @section{Automatically extracting plain values from syntax objects}
     69 
     70 In most cases, you do not have to call @racket[syntax->datum] anymore, as
     71 @racketmodname[subtemplate] implicitly extracts the value of syntax pattern
     72 variables. Do not rely too much on this feature, though, as future versions
     73 may require explicit escapement with a concise shorthand, like
     74 @racket[,pattern-variable] or @RACKET[#,pattern-variable].
     75 
     76 @examples[
     77  #:escape UNSYNTAX
     78  (require racket/require
     79           (for-syntax (subtract-in racket/base subtemplate/override)
     80                       subtemplate/override))
     81  (define-syntax nested
     82    (syntax-parser
     83      [(_ n v)
     84       (if (> n 0) (code:comment "No need for syntax-e")
     85           #`(list (nested #,(sub1 n) v)) (code:comment "No need for syntax-e")
     86           #'v)]))
     87  (nested 5 '(a b c))]
     88 
     89 The implicit @racket[syntax->datum] also works on pattern variables which have
     90 a non-zero ellipsis depth:
     91 
     92 @examples[
     93  (require subtemplate/override)
     94  (syntax-parse #'(1 2 3 4 5)
     95    [(v …)
     96     (define sum (apply + v))
     97     (if (> sum 10)
     98         "foo"
     99         "bar")])]
    100 
    101 @section{Function application enhancements}
    102 
    103 Why bother ourselves with @racket[apply]? Let's just write what we want:
    104 
    105 @examples[
    106  (require subtemplate/override)
    107  (syntax-parse #'(1 2 3 4 5)
    108    [(v …)
    109     (if (> (+ v …) 10)
    110         "foo"
    111         "bar")])]
    112 
    113 Ellipses work as you expect when used in expressions:
    114 
    115 @examples[
    116  (require subtemplate/override)
    117  (define/syntax-parse ((vᵢⱼ …) …) #'((1 2 3 4) (5 6)))
    118  (define/with-syntax (xₖ …) #'(a b c))
    119  (+ vᵢⱼ … …)
    120  (define average (/ (+ vᵢⱼ … …) (length (list vᵢⱼ … …))))
    121  average
    122  (max (min vᵢⱼ …) …)
    123  (list vᵢⱼ … … xₖ …)
    124  (list (list (+ vᵢⱼ 1) …) … (symbol->string xₖ) …)
    125  (list (list vᵢⱼ …) … xₖ …)
    126  (code:comment "Automatically derived symbols:")
    127  (list (list yᵢⱼ …) …)
    128  (list yₖ …)
    129  (code:comment "Same ids as the yₖ ones above:")
    130  #'(yₖ …)
    131  ]
    132 
    133 Here is another trick with ellipses: @racket[((vᵢ …) …)] should normally call
    134 @racket[1] with arguments @racket[2 3 4], and @racket[5] with the argument
    135 @racket[6], and then call the result of the first with the result of the
    136 second as an argument. Since in most cases this is not what you want, the
    137 @racket[list] function is implicitly called when the second element of an
    138 application form is an ellipsis (do not abuse it, the semantics are a bit at
    139 odds with the usual ones in Racket and might be surprising for people reading
    140 your code):
    141 
    142 @examples[
    143  (require subtemplate/override)
    144  (define/syntax-parse ((vᵢⱼ …) …) #'((1 2 3 4) (5 6)))
    145  ((vᵢⱼ …) …)
    146  (vᵢⱼ … …)
    147  (((+ vᵢⱼ 1000) …) …)
    148  (code:comment "Automatically derived symbols:")
    149  ((yᵢⱼ …) …)
    150  (yᵢⱼ … …)]
    151 
    152 Ellipses surprisingly also work on @racket[define],
    153 @racket[define/with-syntax] and @racket[define/syntax-parse]:
    154 
    155 @examples[
    156  (require subtemplate/override)
    157  (define/syntax-parse ((v …) …) #'((1 2 3 4) (5 6)))
    158  (define/syntax-parse (x …) #'("a" "b" "c"))
    159  (begin
    160    (define w (+ v 1)) … …
    161    (define/syntax-parse y:id (string->symbol x)) …)
    162  w
    163  #'(y …)]
    164 
    165 Since the trick is pulled off by a custom @racket[begin] form, provided by
    166 @racketmodname[subtemplate], it will not work as expected at the REPL unless
    167 you explicitly wrap the define and ellipses with a @racket[begin] form, as
    168 done above. Within a module, however, this should work fine.
    169 
    170 @section{Ellipsis-preserving @racket[unsyntax]}
    171 
    172 Racket's @orig:syntax and @orig:template from
    173 @racketmodname[syntax/parse/experimental/template] both forget the current
    174 ellipsis position within an @racket[unsyntax] form. This makes it difficult to
    175 perform simple changes to each element of a pattern variable under ellipses.
    176 @racketmodname[syntax/parse/experimental/template] provides template
    177 metafunctions, but they are unpractical for one-off small-scale alterations.
    178 With @racket[subtemplate], @RACKET[#,e] and @RACKET[#,@e] both preserve the
    179 current ellipsis position, meaning that uses of @racket[syntax],
    180 @racket[quasisyntax], @racket[template] and so on within @racket[e] will use
    181 the currently-focused portion of pattern variables under ellipses.
    182 
    183 @examples[
    184  #:escape UNSYNTAX
    185  (require subtemplate/override racket/list)
    186  (define sd syntax->datum)
    187  (define/syntax-parse ((v …) …) #'((1 2 3 4) (5 6)))
    188  (sd #`(foo #,(+ v …) …))
    189  (code:comment "Quote, escape, re-quote, re-escape, re-quote:")
    190  (sd #`(foo #,(cons (length (syntax->list #'(v …)))
    191                 #`(#,(add1 (syntax-e #'v)) …))
    192         …))
    193  (code:comment "Concise version of the above:")
    194  (sd #`(foo (#,(length (v …)) #,(add1 v) …) …))
    195  (sd #`(foo #,(length (syntax->list #'(v …))) …))
    196  (sd #`(foo #,(length (list v …)) …))
    197  (sd #`(foo (#,(add1 v) …) …))
    198  (sd #`(foo #,(add1 v) … …))
    199  (sd #`(foo #,@(range v) … …))]
    200 
    201 It is still possible to get the traditional full-escape behaviour with
    202 @RACKET[#,,e] instead of @racket[unsyntax], and @RACKET[#,@,e] or
    203 @RACKET[#,,@e] instead of @racket[unsyntax-splicing]:
    204 
    205 @examples[
    206  #:escape UNSYNTAX
    207  (require subtemplate/override racket/list syntax/stx)
    208  (define sd syntax->datum)
    209  (define/syntax-parse ((x v …) …) #'((10 1 2 3 4) (100 5 6)))
    210  x
    211  v
    212  (sd #`(foo (x #,,#'(x …)) …))
    213  (sd #`(foo (x #,,(stx-map (λ (x) (add1 (syntax-e x))) #'(x …))) …))
    214  (sd #`(foo (x #,,(list (list (add1 v) …) …)) …))
    215  (sd #`(foo (x #,,(((add1 v) …) …)) …))
    216  (sd #`(foo (x #,,(stx-map (λ (x) (length (syntax->list x)))
    217                            #'((v …) …))) …))
    218  (sd #`(foo (x #,,((length (v …)) …)) …))
    219  (sd #`(foo ((v …) #,,((length (v …)) …)) …))
    220  (sd #`(foo (x #,,@((length (v …)) …)) …))
    221  (sd #`(foo (x #,@,(range (length (x …)))) …))
    222  (sd #`(foo (v … #,,@((range (length (v …))) …)) …))]
    223 
    224 @section{Splicing and conditional template elements}
    225 
    226 The splicing form @racket[?@] as well as @racket[??] should be familiar to
    227 users of @racketmodname[syntax/parse/experimental/template]. The
    228 @racketmodname[subtemplate] library provides overridden versions which also
    229 work outside of syntax templates, as well as a few extras:
    230 
    231 @examples[
    232  (require subtemplate/override)
    233  (define/syntax-parse ({~optional {~or k:keyword b:boolean i:nat}}
    234                        {~and {~or (v …) s:str}} …)
    235    #'(#:a-keyword (1 2 3 4) "foo" (5 6)))
    236  (list (?? (+ v …)
    237            (string-length s)) …)
    238  (list (?? (?@ v …)
    239            (string-length s)) …)
    240  (list 'x (?@@ '(y y y) (?? (?@ (list 'c v …))) …) 'z)
    241  (list (?if s "string" "list of numbers") …)
    242  (?cond [k (list (?? (?@ 'there-was-a-keyword v …)) …)]
    243         [b (list (?? (?@ 'there-was-a-boolean (?? v s) …)) …)]
    244         [else (list (?? (?@ (?? i) v …)) …)])
    245  (list (?attr k) (?attr b) (?attr i))
    246  (?? k b i 'none)]
    247 
    248 The @racket[?@@] splicing form performs two levels of unwrapping (it can be
    249 understood as a way to perform @racket[(?@ (append elements …))]). The
    250 @racket[(?if _condition _true _false)] is a generalisation of @racket[??],
    251 which accepts a @racket[_condition] template, and produces the
    252 @racket[_true]-template if there are no missing elements in the
    253 @racket[_condition] (in the sense of @racket[~optional]), and produces
    254 @racket[_false] otherwise. @racket[?cond] is a shorthand for a sequence of
    255 nested @racket[?if] forms, and @racket[(?attr a)] returns a boolean indicating
    256 the presence of the attribute (it is a shorthand for @racket[(?if a #t #f)]).
    257 Finally, @racket[??] itself is not limited to two alternatives. When given a
    258 single alternative, @racket[??] implicitly uses @racket[(?@)], i.e. the empty
    259 splice, as the second alternative (this is the behaviour of the version from
    260 @racketmodname[syntax/parse/experimental/template]). When two or more
    261 alternatives are specified, each one is tried in turn, and the last one is
    262 used as a fallback (i.e. an empty splice is @emph{not} implicitly added as a
    263 last alternative when there are already two or more alternatives).
    264 
    265 The @racket[?if] form is useful when one would want to write a @racket[??]
    266 form, where the triggering condition should not appear in the left-hand-side
    267 of @racket[??], for example when changing the generated code based on the
    268 presence of a keyword passed to the macro:
    269 
    270 @examples[
    271  (require racket/require
    272           (for-syntax (subtract-in racket/base subtemplate/override)
    273                       subtemplate/override))
    274  (define-syntax my-sort
    275    (syntax-parser
    276      [(_ {~optional {~and reverse-kw #:reverse}} v …)
    277       #'(sort (list v …) (?if reverse-kw > <))]))
    278  (my-sort 3 2 1)
    279  (my-sort #:reverse 3 2 1)]
    280 
    281 Note that @racket[?@] and @racket[?@@] work on regular lists (but ellipses do
    282 not), and they can splice multiple arguments into the surrounding function
    283 call. One last application trick is the dotted tail argument, used as a
    284 shorthand for @racket[apply]:
    285 
    286 @examples[
    287  (require subtemplate/override racket/function)
    288  (define l '((1 2 3) (4 5 6)))
    289  (vector 'a (?@ l) 'c)
    290  (+ 0 (?@@ (?@@ l)) 7)
    291  (vector 'a (?@@ (?@@ l)) 'c)
    292  (+ 0 (?@@ . l) 7)
    293  (vector 'a (?@@ . l) 'c)
    294  (map + . l)]