On this page:
3.1.1 By-binding keywords
3.1.2 By-name keywords and other alternatives
Version: 4.2.1

3.1 Subforms and Keywords

Synopsis: How to represent special subforms and write macros that interpret them.

Examples: cond and else; class and public, init, etc

Special subforms are typically distinguished by keyword identifiers (not to be confused with keyword values such as #:kw). A subform can consist of such a keyword identifier by itself, but more often the keyword appears in an operator-like position of the subform, followed by additional syntax.
  else
  (public get-color)

Keywords can be recognized either by name (symbolically) or by binding. By-binding keywords are especially preferable in cases when the keyword’s special subform can be used in the same positions as expressions, definitions, or other macro-expandable forms. For example, if cond recognized else symbolically, then in the code
  (let ([else (b? x)])
    (cond [(a? x) __]
          [else __]))
there would be an ambiguity between the else special subform and a test expression consisting of just the local variable else. (The ambiguity would probably be resolved in favor of the special subform. Then the choice of local variable names is significant in this case, against the general principle of alpha-convertability.) Since cond recognizes the else keyword by its binding, there is no ambiguity (although the programmer might still be surprised if the binding of the local else variable is far away).

3.1.1 By-binding keywords

Keywords recognized by binding should be given a binding to recognize. In general, to avoid confusion, the keyword identifier should be defined as a macro that always raises an error when expanded. For example:
  (define-syntax (else stx)
    (raise-syntax-error #f "not allowed as an expression" stx))

Macros recognize by-binding keywords explicitly using free-identifier=? or implicitly using the “literals list” of syntax-rules or syntax-case.

Here’s an example of a “capricious conditional” macro that has two special subforms: the standard else clause and a perhaps clause that randomly decides whether to succeed or fail.

  (module capcond scheme/base
    (require (for-syntax scheme/base))
    (provide perhaps capcond)
  
    (define-syntax (perhaps stx)
      (raise-syntax-error #f "not allowed as an expression" stx))
  
    (define-syntax capcond
      (syntax-rules (else perhaps)
        [(capcond)
         (void)]
        [(capcond [else answer])
         answer]
        [(capcond [perhaps answer] . clauses)
         (if (zero? (random 2))
             answer
             (capcond . clauses))]
        [(capcond [question answer] . clauses)
         (if question
             answer
             (capcond . clauses))])))

It is important for the else and perhaps patterns to occur before the final question pattern. It is also important for perhaps to be provided along with capcond. If perhaps were not provided, then users of capcond in other modules would have no way of writing a perhaps clause. Whatever the local binding of perhaps might be, it would not be the same binding as the one expected by the capcond macro.

Connection: In the example above, a keyword identifier carries no information other than its identity: else is distinct from perhaps, and both are distinct from any definition or expression forms. It is possible to make the keyword identifier itself carry information; this idea leads to the Static Information pattern.

3.1.2 By-name keywords and other alternatives

Recognizing keywords by binding avoids ambiguity and makes keyword identifiers subject to manipulation by standard scoping mechanisms (module import, export, and renaming; and local shadowing). On the other hand, when there is no chance of ambiguity, recognizing keywords by name makes reserving the keywords unnecessary and reduces competition for the limited number of short, apt names. For example, the keywords that tag the #%require form’s raw require spec variants are recognized by name.

Another idiomatic approach is to use actual syntactic keywords (not identifiers) to delineate special subforms.