Macros that Work Together
The first section of this paper discussed the hygienic properties of the Racket macro language. They use the example of the or operator to show the lexical bindings work in Racket. In that example a temp variable is introduced within the or macro, but because of the lexical scope it does not interfere with any other temp variables. Unlike other languages, Racket is actually built upon itself using language towers, which is part of the reason why it has become so powerful. These language towers consist of procedural macros and syntax objects. These language towers are powerful because of the cooperation between macros. This cooperation enables compile-time bindings, sub-form expansion, and definition contexts. The compile-time information from Scheme only included the macro itself, whereas Racket includes the bindings of identifiers to compile-time information. To show how this works they give an example regarding how the define-struct macro provides information to the match pattern-matching form about the shape of structure declarations. This information can be passed using the syntax-local-value function.
Unlike the pattern matching operator match that is used in other macro transformers, Racket's macro transformer provides the syntax-case pattern-matching form. This form allows for the matching of syntax fragments, which was not possible in previous macro languages. One of the difficult parts with implementing the syntax-case is matching the pattern variables to the syntax they are used in in the macro template. This difficulty arises because the syntax-case can take arbitrary expressions. To solve this the Racket macro expander implements syntax-case as a macro that binds the pattern variables to compile-time information. The pattern variables are then replaced by compile-time variables. Another powerful features of Racket is the addition of Classes to the macro world. They use an example of a chicken class to demonstrate how classes can be used. Something that makes these classes different than other macros is its sub-forms are expanded before they produce their results. Concepts of internal definitions are then discussed, along with the Racket features that accommodate them. These features include: partial sub-form expansion, an explicit representation of definition contexts, and an operation to extend a definition context with bindings as the bindings are discovered. These require the implementation of partial expansion on the sub-forms of lambda and class macros. These macros provide a list of identifiers that act as stopping points in the expansion to the local-expand operation. Racket also provides the ability to add packages which add sets of definitions to act as regular bindings. The last additional feature they discuss is that of the DrRacket programming environment. DrRacket preforms all of the macro expansions before it manipulates the code, so it actually only deals with the core language. As we have discussed previously, this allows DrRacket to handle any language extension it is given as long as it can be expanded to the core Racket language.
They next go on to talk about the formal model of the Racket Language. First they talk about the core language, of which everything seemed fairly straightforward. Next they talk about syntax objects. They state that the core language serves two roles: the surface language is parsed into it, and it is the language used for implementing macro transformers. To provide these features, the core language must include syntax objects, which represent the surface-language fragments that macro transformers manipulate. These syntax-objects represent a value along with its lexical-context information. The paper then defines name, variable, symbol, and identifiers. A names are used to represent variables and symbols. Variables are either the formal arguments to functions, or references to function arguments. Symbols represent literal expressions like the quote operator. Identifiers are symbols combined with lexical context. Various other features of the core language are then explained. The parser used in their model is then defined. It converts syntax objects to abstract syntax trees.
In the next section, they discuss the model of their macro expander. Their expander takes in a syntax object for a source program, and returns a syntax object of the expanded program. The syntax object it returns must fit the limited shape of the syntax objects that are recognized by the parser. In other words the expander will have to recognize all the forms the parser recognizes. In order to support macros in general, they have extended the source language to include the let-syntax form, which is a simplified version of Scheme's macro-binding forms. Their let-syntax form binds identifiers to macro transformer functions. In order to track lexical information they introduce lexical-content information into syntax objects. They discuss the challenges to tracking lexical information, along with their corresponding solutions. After discussing the tracking of lexical information they claim that their model is able to handle most macros that are available in Scheme. Then they add two additional primitives that reflect the expanded macro API of Racket: lvalue, and lexpand. The first is for accessing arbitrary compile-time bindings. The second is for forcing the expansion of a sub-form. These new primitives are build into the new macro transformer, and are explained in further detail in the paper. Next they define two new expansion-time primitives used to support definition contexts: new-defs, and def-bind. The former creates a new context, and the ladder binds names in a context. These are also then explained in further depth.
The main contribution put forth by this paper is the formal models of Racket, and how they are used to implement the language. Essentially it seems as though this paper is the argument for switching to Racket from Scheme or Lisp. I feel like I got a basic understanding of how everything works together to create the Racket language. Section two on many of the different features of Racket was fairly clear due to the fact that we had previously discussed many of these features in class. The formal model of Racket was a little more burdensome to try and understand, mainly due to the length of section 3. I did like how the authors broke up the formal model into smaller sections with explanations on each sections. This made it much simpler to follow than flipping back and forth between a large page of semantics and the explanation later on in the paper as has been done in most other papers. Due to time constraints, when I ran into things I didn't understand, I just moved on to the next sentence and took their word for it. For this reason I don't have any specific questions to propose before class tomorrow.