Debugging Hygienic Macros
What’s it about?
This paper attempts to highlight the usefulness of macros, but then highlights the inherent difficulties that come up when macros are used and errors invariably appear (especially in more complicated or intertwined examples). This paper works to propose a system for making macros more understandable in a debugging context by providing a way for a user to selectively expand them in an intuitive and useful manner.
What does this paper contribute?
This paper begins by acknowledge the power of macros and the important place a debugger can have when developing with a programming language. This especially true when the problems that are being encountered are especially difficult to understand, or similarly if the programmer’s experience level with the language features being used (e.g. macros) makes the errors for that user difficult to deduce, even if their cause is rather simple if one understands how things are operating. Macros are an area where this need is even more highlighted because their expansion can bear little resemblance to the shape or syntax of their unexpanded form and thus error details in macro usage can either appear as nonsensical behavior (from the user’s perspective) or error reporting from deep within the implementation of the macro and thus be very difficult to utilize to solve the problem at hand. The user needs to be able to debug/attack their problem, they note, from the level of abstraction that they are working and thinking at to be most effective.
They then go on to introduce the macro-stepper they have designed for Racket thus far and highlight the various features it has and how they address the needs they alluded to when debugging macros. This macro stepper operates not by simply expanding *all* macros it comes across but instead takes the expression tree and expands it at some specified level that is most useful for the user. This can come in the form of not expanding Racket syntax or library syntax broadly or more specifically the user can specify which macros should be opaque currently and thus not expanded during the process of stepping through expansion. Doing this allows the user to reason about their problem at the level that is most appropriate and allows them to adjust this as needed (pretty cool!).
They also get into some of the specific details of how this information is injected into the macro expander (the events notifying a potential debugger when some expansion is entered or exited, etc...) and discuss the soundness of this process (relative to the original expansion process), noting it is just a small syntactic change to the big-step semantics of the expander.
Fortifying Macros
What is it about?
This paper is interested in the current dichotomy of macro definitions that appears to exist. The authors suggest that currently macros are either designed to be very clear and understandable, or they are designed to be very robust, but that doing both is impractical currently. They wish to resolve this discrepancy.
What does this paper contribute?
This paper contributes a way to combine the clarity and robustness one would desire for their macro definition but currently is unable to achieve. Currently, the authors suggest, the Macro-By-Example method produces very clear macro definitions which get parsing code automatically generated. These are great, but currently lack the ability to do more complicated verification of the terms given to the macro (e.g. the duplicate id example for let). The solution to some of this verification is to have guards (fenders?) which return a bool reporting if the constraints are met or not. These can verify some things, but their error reporting is very unhelpful (it is not bound to *where* or *why* there was a problem within the macro). They also duplicate code potentially as well as work. The other other option is to write an extremely detailed, self verifying macro, however this feels wrong and seems to violate the entire purpose of macros, forcing you into some design pattern for something that should be able to be stated simply (since the concept, at a high level, is very straightforward).
The authors’ solution to this conundrum is to introduce a new method for defining macros (syntax-parse) as well as syntax-classes which abstracts syntactic ideas and constraints that are ubiquitous into their abstract ideas. This method they introduce is quite amazing in what it provides: it gives more expressive patterns, reduces duplicate definitions, allows guards to be removed in favor of more expressive ‘side conditions’ and annotations which actually report where failure occurred during macro expansion so the user may understand what is actually going wrong at the level which they are working (instead of in obscure, hidden implementation details).
They go on to describe some of the more interesting situations of debugging complex macros. There is a let which is overloaded to handle both traditional let and named-let semantics, and they define “progress” as how far the term was matched and utilize this measurement to decide, for example, whether the error (assuming there was one) came from the let or named-let branch of the let macro in question. Later they go into detail about how they expanded the expressiveness of their macro definitions by showing the patterns and logic behind their syntax-parse and examine some case studies of how syntax-parse has affected popular macros (loop and parsing, namely).