www

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

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}