Notes by Ed Kademan <email@example.com>
The third meeting of Scheme Boston took place at Northeastern University on Tuesday night, November 19, 2002. Approximately 20 people showed up to hear Neel Krishnaswami give a talk entitled, "A Hacker's Guide to Hygienic Macros."
Neel discussed only the standard types of macros as described in the Revised Report (R5RS). That is, he restricted himself to the `define-syntax' and `syntax-rules' forms, and did not attempt to cover `syntax-case' or any of the varieties of `defmacro' that some schemes implement. The talk consisted of a series of progressively more complicated examples designed to illustrate the features of both the pattern language that syntax-rules processes and the transformer that it generates. These features include literal identifiers, ellipses (...) for indicating an indefinite number of forms to match, multiple pattern/template pairs, and the use of recursion.
Although basic the tutorial generated some interesting discussion about the nature of hygiene and referential transparency, and so rather than simply recapitulate the material on Neel's slides I would like to try to summarize a couple of points that he and others made in connection to them. I didn't understand everything that was said so whether you were present or not please let me know of anything I get wrong.
One of the major themes was that hygiene and referential transparency have a sort of dual nature. On the one hand a macro expands in such a way that it does not capture or interfere with the forms that are its arguments. That is, the expansion does not introduce problems for the code at the "expansion point." But at the same time code at the expansion point cannot interfere with the forms in the macro. The system maintains an overall "lexical integrity"---to quote Ken Shan.
For example, one of the macros that Neel presented was an implementation of a for-loop.
> (define-syntax pascal-for (syntax-rules (from to) ((_ (i from lower-bound to upper-bound) body) (let ((lower lower-bound) (upper upper-bound)) (let loop ((i lower)) (if (<= i upper) (begin body (loop (+ i 1))))))))) > > (define lower 10) > (define i 1000) > (pascal-for (i from 1 to 3) (format #t "i is ~A and lower is ~A~%" i lower)) i is 1 and lower is 10 i is 2 and lower is 10 i is 3 and lower is 10 > i 1000
The template of the macro binds a variable called "lower" but it is as though this new binding is not in effect when the form within the loop executes. The pascal-for users can feel free to choose any kind of variable names they want without worrying about the expansion clobbering them. Likewise the loop index i which is an argument to the macro---as it were---reverts back to its original binding after the invocation.
So the above macro does not interfere with the code at the expansion point. Another of Neel's examples shows how the code at the expansion point cannot interfere with the macro. Consider
> (define-syntax kons (syntax-rules () ((_ e1 e2) (cons e1 e2)))) > > (let ((cons 'a)) (kons 'b 'c)) '(b . c)
The macro expands to use the "cons" that was in effect at the time of definition and not at the time of invocation. The intention is that the whole syntax extension system just automatically works as you'd expect for a lexically scoped language.
At one point Matthias Felleisen remarked that there are three reasons---that he knows of---for using macros instead of procedures. He emphasized that it's possible that the three are not exhaustive, and in the ensuing discussion it became obvious that they are not perfectly orthogonal. They are:
An example of 3 would be a macro-based implementation of the "case" form in scheme.
The next meeting is tentatively scheduled for December 17th at which time Ken Williams will describe his efforts to write a program for solving a three-dimensional puzzle. The presentation may touch on backtracking and continuations. There was some discussion about moving the date for the next meeting and possibly for switching to another night of the week in the future but I think that the overall feeling was that we wouldn't do that unless the mailing list as a whole reached a consensus on an alternative.