subtemplate.scrbl (19803B)
1 #lang scribble/manual 2 @require[racket/require 3 scriblib/footnote 4 scribble-math 5 "orig.rkt" 6 @for-label[subtemplate 7 (only-in syntax/parse/experimental/template) 8 (subtract-in racket/base subtemplate)]] 9 10 @title[#:style (with-html5 manual-doc-style)]{Subtemplate} 11 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 12 13 This library should be considered experimental. Although most of the syntax 14 should work in the same way in future versions, the behaviour of some corner 15 cases may change, as I try to find the best semantics. 16 17 Also, this library requires some patched versions of @racket[syntax-parse] and 18 @orig:syntax-case, as these do not offer the hooks needed to implement 19 @racket[subtemplate]. Unfortunately, as the official implementations of 20 @racket[syntax-parse] and @racket[syntax-case] evolve, compatibility issues 21 may arise. 22 23 If you desire to use this library, please drop me an e-mail (my address is 24 below the title), so that I can keep you informed of upcoming changes, see if 25 these are likely to cause problems in your code. 26 27 Finally, If the maintenance burden is too high, I might drop the compatibility 28 with @racketmodname[syntax/parse] and @|orig:syntax-case|. 29 30 @include-section{examples.scrbl} 31 32 @section{The main @racketmodname[subtemplate] module} 33 34 @defmodule[subtemplate]{ 35 The @racketmodname[subtemplate] module provides @racket[subtemplate], an 36 alternative to @racketmodname[syntax/parse]'s @orig:template, which 37 supports several convenience features: 38 39 @itemlist[ 40 @item{When an identifier @racket[yᵢ] is encountered in a template, it is 41 automatically defined as a pattern variable containing temporary identifiers. 42 43 This avoids the need to manually call @racket[generate-temporaries]. 44 45 The generated temporary identifiers will be based on a @racket[xᵢ] pattern 46 variable with the same subscript as @racket[yᵢ], and @racket[yᵢ] will be 47 nested at the same ellipsis depth as @racket[xᵢ]. 48 49 The identifiers @racket[xᵢ] and @racket[yᵢ] must end wit the same subscript, 50 which must be a sequence of unicode subscript characters picked among 51 @tt{ₐ ₑ ₕ ᵢ ⱼ ₖ ₗ ₘ ₙ ₒ ₚ ᵣ ₛ ₜ ᵤ ᵥ ₓ ᵦ ᵧ ᵨ ᵩ ᵪ}. Alternatively, the 52 subscript may be specified with an underscore followed by any characters 53 other than an underscore. The two notations are equivalent, in the sense that 54 @racket[yᵦ] and @racket[y_β] are interpreted in the same way, and if both 55 appear within a template, they will use the same sequence of temporary 56 identifiers generated from an @racket[xᵦ] or @racket[x_β].} 57 @item{The value of pattern variables is automatically extracted when the 58 variable does not appear in a syntax template. Note that since the syntax 59 object is transformed into a plain datum, source locations and lexical 60 contexts are lost. This is a trade-off between better error messages, which 61 make sure that source locations and lexical context are not lost by accident 62 (no automatic @racket[syntax-e]), and more concise code (with automatic 63 @racket[syntax-e]). It is possible that a future version may require explicit 64 syntax-e, possibly via a concise shorthand like @racket[unquote] (@tt{,}) or 65 @racket[unsyntax] (@tt{#,}), if this feature turns out to be too dangerous in 66 practice.} 67 @item{Ellipses work outside of syntax templates, and can be used after 68 definitions and expressions. 69 @itemlist[ 70 @item{The result of an expression under @${n} ellipses is a 71 @${\text{nested}^n} list, where the expression is evaluated for 72 each value of the pattern variables located within. In other words, 73 @racket[(x ...)] should produce a value similar to 74 @racket[(syntax->datum #'(x ...))]. However, it is possible to actually 75 manipulate the value, e.g. by writing @racket[(+ x 1) ...]. It is possible 76 to write @${m} ellipses in a row (which has the effect of flattening 77 @${m - 1} levels in the result list). It is also possible to nest the use 78 of these ellipses, e.g. with @racket[(x ...) ...], which keeps the 79 structure of the nested lists in the result.} 80 @item{When a definition form (@racket[define], @racket[define/with-syntax] or 81 @racket[define/syntax-parse] for now) is followed by @${n} ellipses, then 82 the defined identifier is a @${\text{nested}^n} list, or a syntax pattern 83 variable with an ellipsis depth of @${n}. The expression is evaluated for 84 each value of the template variables it contains. Note that the structure 85 of the nested lists is not flattened, despite the fact that the ellipses 86 are written one after another. This is because it is usually the desired 87 outcome, and nesting parentheses around the definition form would produce 88 rather unreadable code.} 89 @item{These ellipses can also be used ``inline'' within function calls 90 (@racketmodname[subtemplate] overrides @racket[#%app] to achieve this). For 91 example: @racket[(/ (+ x ...) (length x))] would compute the average of 92 @racket[(syntax->datum #'(x ...))]} 93 @item{Subscripted identifiers should also work in expressions 94 (@racketmodname[subtemplate] overrides @racket[#%top] to achieve this), 95 although this seems less useful, as the temporary identifiers loose their 96 lexical context information in that way.} 97 @item{The splicing forms @racket[?@] and @racket[?@@], as well as 98 @racket[??], @racket[?if] and @racket[?cond] work within expressions, and 99 can be used to splice values into the argument list when calling a 100 function. For example, @racket[(+ 1 (?@ l) 3)] would splice the values of 101 the list @racket[l] into the argument list, effectively calling 102 @racket[(+ 1 97 98 99 3)] if @racket[l] was equal to 103 @racket[#'(97 98 99)]. Additionnally, it is possible to append a list 104 at the end of the argument list, with the syntax @racket[(+ 1 2 . l)].} 105 @item{It is possible to create a syntax object based on one of the iterated 106 pattern variables within the expression-ellipses, for example using 107 @racket[(#'x ...)].}]} 108 @item{Within a @racket[subtemplate] and a @racket[quasisubtemplate], it is 109 possible to use @racket[unsyntax] and @racket[unsyntax-splicing] to escape 110 from the template. Within the escaped expression, the ellipsis depth of the 111 template is conserved, making it possible to write 112 @RACKET[(subtemplate (#,(+ x 1) ...))], for example. 113 114 The usual behaviour, which resets the ellipsis count to 0, can be obtained 115 with @RACKET[#,,expr] (that is, 116 @racket[(#,(racket unsyntax) (#,(racket unquote) expr))]) for an 117 @racket[unsyntax]-like escape. An @racket[unsyntax-splicing]-style escape can 118 be obtained with @RACKET[#,,@expr] or @RACKET[#,@,expr] (that is, 119 @racket[(#,(racket unsyntax) (#,(racket unquote-splicing) expr))] or 120 @racket[(#,(racket unsyntax-splicing) (#,(racket unquote) expr))]).} 121 @item{Several utilities in the spirit of @racket[??] and @racket[?@] are 122 provided, namely @racket[?@@], @racket[?attr] @racket[?cond] and 123 @racket[?if].} 124 @item{All features (subscripted identifiers, dotted expressions and 125 definitions, and the ellipsis-preserving @racket[unsyntax]) should work well 126 with omitted elements in attributes, as created by 127 @racket[~optional] or @racket[~or] in @racket[syntax-parse].}]} 128 129 @subsection{Modules re-provided by @racketmodname[subtemplate]} 130 131 The @racketmodname[subtemplate] library needs some cooperation from 132 @racket[syntax-case], @racket[syntax-parse] and similar forms. For this 133 reason, some patched versions are defined in the @racketmodname[stxparse-info] 134 library. @racket[subtemplate] cannot work properly if the right modules are 135 loaded. To make it easier to use @racketmodname[subtemplate], it re-provides 136 the modules that need to be loaded for it to function properly. 137 138 The @racketmodname[subtemplate] module re-provides 139 @racketmodname[stxparse-info/parse], @racketmodname[stxparse-info/case] and 140 the parts of @racketmodname[racket/syntax] which are not overridden by 141 @racketmodname[stxparse-info/case]. 142 143 The @racketmodname[subtemplate/private/override] module also re-provides 144 @racketmodname[stxparse-info/parse/experimental/template], but without 145 @orig:template, @orig:quasitemplate, @orig:?? and @orig:?@, which are remapped 146 to their equivalents from this library, and without @orig:template/loc] and 147 @orig:quasitemplate/loc, which do not have an equivalent yet. 148 149 @subsection{New and overridden bindings provided by @racketmodname[subtemplate]} 150 151 @defform*[{(subtemplate tmpl) 152 (subtemplate tmpl #:properties (prop ...))} 153 #:contracts 154 ([prop identifier?])]{ 155 156 Like @orig:template from @racketmodname[syntax/parse/experimental/template], 157 but automatically derives identifiers for any @racket[yᵢ …] which is not bound 158 as a syntax pattern variable, based on a corresponding @racket[xᵢ …] which is 159 bound as a syntax pattern variable. Additionally, @racket[subtemplate] 160 supports a number of features described in 161 @secref["The_main_subtemplate_module" 162 #:doc '(lib "subtemplate/scribblings/subtemplate.scrbl")], 163 which are not part of @racketmodname[syntax/parse/experimental/template]} 164 165 @defform*[{(quasisubtemplate tmpl) 166 (quasisubtemplate tmpl #:properties (prop ...))} 167 #:contracts 168 ([prop identifier?])]{ 169 Like @orig:quasitemplate from 170 @racketmodname[syntax/parse/experimental/template], but automatically derives 171 identifiers for any @racket[yᵢ …] which is not bound as a syntax pattern 172 variable, based on a corresponding @racket[xᵢ …] which is bound as a syntax 173 pattern variable, in the same way as @racket[subtemplate]. Additionally, 174 @racket[quasisubtemplate] supports a number of features described in 175 @secref["The_main_subtemplate_module" 176 #:doc '(lib "subtemplate/scribblings/subtemplate.scrbl")], 177 which are not part of @racketmodname[syntax/parse/experimental/template]} 178 179 @defform*[{(template tmpl) 180 (template tmpl #:properties (prop ...))} 181 #:contracts 182 ([prop identifier?])]{ 183 184 Like @racket[subtemplate], but does not automatically generate pattern 185 variables based on their subscript. The other features still work 186 (ellipsis-preserving escapes with @racket[unsyntax], support for @racket[?@@], 187 @racket[?attr], @racket[?cond] and @racket[?if]).} 188 189 @defform*[{(quasitemplate tmpl) 190 (quasitemplate tmpl #:properties (prop ...))} 191 #:contracts 192 ([prop identifier?])]{ 193 194 Like @racket[quasisubtemplate], but does not automatically generate pattern 195 variables based on their subscript. The other features still work 196 (ellipsis-preserving escapes with @racket[unsyntax], support for @racket[?@@], 197 @racket[?attr], @racket[?cond] and @racket[?if]).} 198 199 @defform[#:kind "procedure" 200 (?@ . expr)]{Splices the @racket[expr] into the surrounding form 201 (which must be a function application). If the surrounding form is a 202 @racket[begin], @racket[let], or @racket[#%intdef-begin], then the the 203 splicing lists are not processed, but may be processed later by using the 204 splicing-list value as an argument to a function. 205 206 Also works in @racket[template], @racket[subtemplate] and their derivatives.} 207 @defform[#:kind "procedure" 208 (?@@ . expr)]{Appends all the lists contained within @racket[expr], 209 and splices the resulting list into the surrounding form. If the 210 surrounding form is a @racket[begin], @racket[let], or 211 @racket[#%intdef-begin], then the splicing lists are not processed, but may be 212 processed later by using the splicing-list value as an argument to a 213 function. 214 215 Also works in @racket[template], @racket[subtemplate] and their derivatives.} 216 @defform*[[(?? alt) 217 (?? alt ...+ else)]]{ 218 Executes @racket[alt], if none of the template variables within is omitted 219 (i.e. bound to @racket[#false] for the current ellipsis iteration). Otherwise, 220 the next @racket[alt] is considered. If every @racket[alt] contains omitted 221 template variables, then @racket[else] is excuted. If only one @racket[alt] is 222 specified, without an @racket[else], then @racket[else] defaults to 223 @racket[(?@)], i.e. the empty splice. 224 225 Also works in @racket[template], @racket[subtemplate] and their derivatives.} 226 227 @defform*[[(?if condition alt) 228 (?if condition alt else)]]{ 229 Generalisation of @racket[??]. If none of the template variables within 230 @racket[condition] is omitted (i.e. bound to @racket[#false] for the current 231 ellipsis iteration), then @racket[alt] is executed. Otherwise, @racket[else] 232 is executed. 233 234 Also works in @racket[template], @racket[subtemplate] and their derivatives.} 235 236 @defform[(?attr condition)]{Shorthand for @racket[?if condition #t #f] 237 238 Also works in @racket[template], @racket[subtemplate] and their derivatives.} 239 240 @defform*[#:literals (else) 241 [(?cond [condition alt] …) 242 (?cond [condition alt] … [else alt])]]{ 243 Equivalent to nested uses of @racket[?if]. If no @racket[else] clause is 244 supplied, then @racket[(?@)], i.e. the empty splice, is used instead.} 245 246 @defform[(begin body ...)]{ 247 Overridden version of @|orig:begin|. Supports ellipses after definitions 248 (using @racket[define], @racket[define/with-syntax] or 249 @racket[define/syntax-parse]). Supports ellipses after expressions, in which 250 case the results are grouped into a splicing list, which makes it possible to 251 write @racket[(+ (begin x ...))] and obtain the same result as with 252 @racket[(+ x ...)]. 253 254 @history[ 255 #:changed "1.2" 256 @elem{Added support @racket[define/syntax-parse], fixed documentation which 257 incorrectly claimed support for @racket[define-syntax] instead of 258 @racket[define/with-syntax]}]} 259 260 @defform*[[(let ([var val] …) . body) 261 (let name ([var val] …) . body)]]{ 262 Overridden version of @|orig:let|. Supports ellipses in the @racket[body] 263 after definitions (using @racket[define] and @racket[define-syntax]). Supports 264 ellipses after expressions both in the @racket[body] and in the @racket[val]. 265 In both cases, the results are grouped into a splicing list, which makes it 266 possible to write @racket[(let ([vs x ...]) (+ vs))] and obtain the same 267 result as with @racket[(+ x ...)].} 268 269 @defform[(#%intdef-begin . body)]{ 270 Equivalent to @racket[begin] from @racketmodname[subtemplate], but assumes 271 that it appears directly within the body of a @racket[let] or similar form. 272 273 Third-party macros can cooperate with @racketmodname[subtemplate], allowing 274 its features to be used where a sequence of statements is expected. To achieve 275 that, the macro would need to detect with @racket[identifier-binding] and 276 @racket[syntax-local-introduce] whether @racket[#%intdef-begin] is bound at the 277 macro's use-site. If this is the case, then the macro could use 278 @racket[#%intdef-begin] instead of @racket[begin].} 279 280 @defform*[[(#%app f arg ... . rest) 281 (#%app val ooo ...+ expression ...+ . rest)]]{ 282 Overridden version of @|orig:#%app|, which supports ellipses in the argument 283 list. When one of the arguments contains a splicing list, the list's values 284 are spliced into the argument list. 285 286 If the first argument is an ellipsis, the @racket[list] function is 287 implicitly used, and the first element following @racket[#%app] is interpreted 288 as an argument under ellipses. 289 290 A variable appearing in tail position after a dot is appended to the argument 291 list, and splicing-lists within are handled.} 292 293 @defform[(#%top . var)]{Overridden version of @|orig:#%top|, which is used to 294 automatically derive temporary identifiers in expressions. When an unbound 295 variable @racket[yᵢ] is used and a matching pattern variable @racket[xᵢ] with 296 the same subscript is bound. Note that if a variable @racket[yᵢ] is already 297 bound to some value, no attempt will be made to derive temporary identifiers 298 for that variable. In contrast, if the identifier @racket[yᵢ] appears, quoted 299 by a @racket[subtemplate], then subtemplate will attempt to derive it even if 300 it is bound (unless it is bound as a pattern variable).} 301 302 @defidform[…]{Alias for @racket[...]} 303 @defidform[…+]{Alias for @racket[...+]} 304 305 306 @section{Overriding the default @racket[#'…] and @racket[#`…]} 307 308 @defmodule[subtemplate/override]{ 309 The @racketmodname[subtemplate/override] module provides the same bindings as 310 @racketmodname[subtemplate], but also re-provides @racket[subtemplate] as 311 @racket[syntax], and @racket[quasisubtemplate] as @racket[quasisyntax]. This 312 allows @racketmodname[subtemplate] to be used via the reader shorthands 313 @racket[#'…] and @racket[#`…].} 314 315 @section{Limitations} 316 317 The derived subscripted identifiers have to be syntactically present within 318 the template. In particular, if a template metafunction generates a part of a 319 template containing @racket[yᵢ], it will work only if @racket[yᵢ] is also 320 present in the "main" part of the template (possibly as an argument to the 321 template metafunction, or elsewhere). 322 323 Currently, template metafunctions defined with 324 @racketmodname[stxparse-info/parse/experimental/template] are not compatible 325 with those from @racketmodname[syntax/parse/experimental/template], and vice 326 versa. There is a pending pull request to make some level of compatibility 327 possible, so this problem should hopefully be fixed sometime soon. 328 329 More generally, there might still be some incompatibilities between 330 @racketmodname[stxparse-info/parse] and @racketmodname[syntax/parse] (aside 331 from the fact that @racket[subtemplate] cannot derive @racket[yᵢ] from 332 @racket[xᵢ] if @racket[xᵢ] was defined by the ``official'' 333 @racketmodname[syntax/parse]), please report them to 334 @url{https://github.com/jsmaniac/subtemplate/issues}. 335 336 The code generated by @racket[subtemplate] is not optimised, so compile-time 337 and run-time performance will not be as good as with @racket[syntax] or 338 @racket[template]. 339 340 The expression splicing-lists are not recognised by templates, and it is not 341 possible for a template to produce a ``splicing syntax object'' (instead, an 342 error is raised if a @racket[?@] causes a template to return more than one 343 syntax object). 344 345 Despite the rather extensive test suite, there are likely still some bugs 346 lurking, please report them to 347 @url{https://github.com/jsmaniac/subtemplate/issues}. 348 349 @subsection{Omitted elements in attributes (via @racket[~optional])} 350 351 When some values are missing in the ellipses of a template variable, e.g. via 352 @racket[~optional], @racket[subtemplate] combines all the existing bound 353 variables it can find with the correct subscript, in order to fill in as many 354 elements of the derived variable. For example, if 355 @racket[(attribute xᵢ)]@note{For readability reasons, we note @racket['(x y)] 356 instead of @racket[(list #'x #'y)] here.} returns 357 @racket['((a #f #f) #f (g h i) #f)], and @racket[(attribute yᵢ)] returns 358 @racket['(#f (4 5 6) (7 8 9) #f)], then for a derived @racket[zᵢ … …], 359 @racket[(attribute zᵢ)] will contain 360 @racket['((a/z xᵢ79/z xᵢ80/z) (4/z 5/z 6/z) (g/z h/z i/z) #f)]. The last 361 element is @racket[#f], as @racket[subtemplate] lacks enough information to 362 determine how many elements should be present within the list. The 363 fully-nested @racket[#f] in @racket['(a #f #f)] are derived, as it is clear at 364 that point that there is place for only a single omitted element. 365 366 If new pattern variables with the same subscript are introduced after a 367 generated variable was used, they should have the same structure (i.e. missing 368 sublists in the same positions). Otherwise, the derived variable generated by 369 @racket[subtemplate] would not contain the same elements before and after that 370 new pattern variable was introduced. 371 372 @include-section{light.scrbl}