1.2 Lexical Context Analysis
Synopsis: A syntax template implicitly closes over the bindings of its lexical context. Consequently, by reasoning about the context of a syntax template, one can predict the meanings of references in program fragments it produces.
A syntax template’s lexical context records the association of names to the specific binding occurrences of those names in scope where the template occurs in the program. Lexical context analysis is the process of reasoning about the bindings in the context of a syntax template to predict the meanings of references in program fragments it produces.
An important concern when using syntax templates is making sure their lexical context has the right bindings. Generally this means ensuring that the enclosing module either contains the appropriate definitions or imports the appropriate modules. Either way, the bindings must not be shadowed either by local bindings or pattern variable bindings. (DrScheme’s Check Syntax tool helps illustrate this with its “tentative binding” purple arrows.) The bindings must also be in the correct phase, and the correct phase depends on how the enclosing module is imported (ie, normally, for-syntax, etc).
The rest of this section discusses basic lexical context analysis. Unresolved pattern tag: "multi-phase-lexical-context-analysis" extends the analysis to multiple phases and module graphs involving for-syntax and for-template requires. See other pattern sections for applications of the analysis. In particular, see see the Code Generators pattern for an explanation of phases and scoping in the context of macros and their template-containing auxiliary procedures, and see the Templates for eval pattern for an explanation of phases and scoping for syntax evaluated using eval rather than used in a macro transformation.
1.2.1 Cataloguing the lexical context
The simplest part of lexical context analysis is the lexical context catalogue: a listing of the names in scope with their nearest enclosing binding occurrences.
(module a scheme/base |
(define one 1) |
(define two 2) |
(define-syntax-rule (three) (+ one two)) |
(define four (* two two))) |
one bound by the first definition
two bound by the second definition
three bound by the third definition – don’t forget the enclosing definition itself!
four bound by the fourth definition, even though it’s below the template
add1 from scheme/base
append from scheme/base
...
+ from scheme/base
Okay, so the point is that the full catalogue is huge, and contains things that we couldn’t possibly care about (in fact, this is not quite true – see Unresolved pattern tag: "unhygienic-indirect-reference" – but it’s true enough for now). In general, one does not actually enumerate the complete catalogue, or even part of it; rather, one just focuses on the identifiers relevant to the template at hand.
Aside: Some of the bindings in the catalogue above are ordinary bindings and some are syntax bindings. We don’t consider them part of the catalogue per se, but of course once we know the relevant binding occurrence of a name we can inspect the binding form for such information and more.
one bound by the first definition
two bound by the second definition
+ from scheme/base
So far, so good. Now let’s talk about how to interpret this information.
1.2.2 Interpretation
(module a scheme/base |
(define one 1) |
(define two 2) |
(define-syntax-rule (three) (+ one two)) |
(define four (* two two))) |
The template is the right-hand side of a macro definition. Consequently, it represents the result of the macro. So the desired interpretation of the template is the same as the desired interpretation of the macro.
The desired interpretation of the template is an expression that computes the addition of the module variables one and two.
That is, we want + to refer to the standard addition procedure, + from scheme/base, and we want one to be bound to the definition of one within the module, and likewise for two. The catalogue tells us that they are.
Trivial, no?
1.2.3 Local bindings
Catalogues can involve local bindings as well. Here is an example:
(module c scheme/base |
((let ([one 1]) |
(lambda (two) |
(let-syntax ([three (syntax-rules () |
[(three) (+ one two)])]) |
(three)))) |
2)) |
Here’s the catalogue:
one bound by the let form
two bound by the lambda form
and everything from scheme/base, as before
Note this time that three is not in the catalogue; if we’d bound the macro using letrec-syntax it would be.
The interpretation is the same, except that it refers to the two local variables. The binding of + is the same.
1.2.4 Auxiliary templates
???
Templates with non-expr roles.
(But don’t talk about phases.)
1.2.5 General
What role does the syntax template play?
What is the syntax template’s desired interpretation?
What are the references? What bindings should they refer to?
For templates that are the immediate right-hand sides of macros, the role is usually “an expression” or “a definition.” Templates involved in computations, either within a macro or in an auxiliary outside of it, can have other roles, such as “cond clause” or “list of method bindings.”