Summary
This episode is entirely devoted to Scheme macros from a personal point of view. Pattern matching is introduced as the fundamental mechanism on which macros
are built.
Scheme macros have many faces. You can see them
as a general mechanism to extend the syntax of base Scheme, and
also as a mechanism to reduce boilerplate.
On the other hand, if you focus your attention on the fact that they work at
compile time, you can see them as a mechanism to perform arbitrary computations
at compile time, including compile time checks.
I think the correct way of looking at macros is to see them as a
general facility to write compilers for micro-languages - or Domain
Specific Languages, DSL - embedded in the Scheme language. The
languages defined through macros can be very different from Scheme;
for instance you can define object oriented languages (object systems
such as TinyCLOS or Swindle are typical examples) or even
languages with static typing (the new language Typed Scheme, built
on top of PLT Scheme, is such an example).
In order to address such use cases, Scheme macros
have to be extremely advanced, much more than Lisp macros and any
other kind of macros I know of; as a consequence, they also have a reputation
for complexity. Unfortunately, on top of the intrinsic complexity,
Scheme macros also suffers from accidental complexity, due to history
(there are too many standard macro systems) and to the
tradition of not caring about readability (many Scheme constructs are
made to look more complex than they actually are).
Scheme has two macro systems
included in the de jure standard - syntax-rules,
which allows to define hygienic macros only, and syntax-case, which
has the full power of Lisp macros and much more - plus a de facto standard
based on define-macro system, which is available
in all implementations and it is well known to everybody because of
its strict similarity to Common Lisp defmacro system.
Since there are so many macro systems it is difficult to decide from where
to start in a pedagogical paper or tutorial.
If you look at the original Italian version of this paper, you will
see that I did talk about syntax-rules macros
first. However, after a lot of thinking, I have decided to go my own
way in this English series of the Adventures. Here I will not
discuss syntax-rules, nor I will discuss syntax-case: instead,
I will discuss my own version of Scheme macros, which I called
sweet-macros.
Why I am doing that? After all, why my readers should study my own
version of macros when they surely will be better served off by
learning the standard macrology used by everybody? I have spent
years debating with myself this very question, but at the end
I have decided to go this way for a series of reasons:
I regard the existence of two separate macro systems in the same
standard as a wart of Scheme and as a mistake made by the R6RS
editors: they should have included in the language syntax-case
only, leaving syntax-rules as a compatibility library
built on top of syntax-case;
I really don't like the syntax-case syntax, it is by far too
verbose and unreadable; I find there is a strong need for some
sugar on top of it and that is what sweet-macros are for;
sweet-macros are very close to syntax-case macros, so
once you understand them you will understand syntax-case too;
from there, understanding syntax-rules is a breeze;
starting from sweet-macros is much better from pedagogical
purposes, especially for readers with a Common Lisp background,
since it is easy to explain the relation with defmacro and
the hygiene issue;
my target readers are programmers coming from the scripting
languages (Perl/Python/Ruby) world. For this kind of public, with
no previous exposition to Scheme, bare syntax-case is
just too hard, so I needed to dress it in nice clothes to make it
palatable;
sweet-macros are intended to easier to use than syntax-case
macros, but they are also more powerful, since they provide
introspection and debugging capabilities as well as guarded
patterns, so they should look attractive to
experienced users too; however, this is a nice side effect and not the
main motivation for the library;
sweet-macros were written
expressely for this series of papers, since I did not want to litter
my explanation of Scheme macros with endless rants. So, I took
action and I wrote
my own library of macros made "right": this is also a tribute to
the power of Scheme macros, since you can "fix" them from within
the standard macro framework in fifty lines of code.
If you are an advanced reader, i.e. a Schemer knowing
syntax-case/syntax-rules or a Lisper knowing defmacro, I a am
sure you will ask yourself what are the differences of
sweet-macros with respect to the system you know. I will make a
comparison of the various systems in the future, in episode #12 and
later on. For the moment, you will have to wait. I do not want to
confuse my primary target of readers by discussing other macro systems
right now. I also defer to episode #12 the delicate question are
macros a good idea?. For the moment, focus on what macros are and
how you can use them. Then you will decide if they are a good idea or
not.
My sweet-macros library is a small wrapper
around the syntax-case macro system. I release it
under a liberal BSD licence. You can do whatever you want with it,
just keep the attribution right.
The primary goal of
sweet-macros is semplicity, so it only exports three macros,
def-syntax, syntax-match and syntax-expand:
def-syntax is a macro used to define simple macros, which is
similar to defmacro, but simpler and strictly more powerful.
syntax-match is a macro used to define complex macro transformers.
It is implemented as a thin layer of sugar on top of syntax-case.
syntax-expand is a macro which acts as a debugging
facility to expand macros defined via def-syntax or
syntax-match.
It should be mentioned that standard Scheme macros do not
provide debugging and/or introspection facilities and that
every implementation provides different means of debugging
macros. This is unfortunate, since debugging macros is usually difficult
and it is done often, since it is uncommon to get a
macro right the first time, even if you are an experienced developer.
I wanted to provide my readers with the tools to
understand what they are doing, without relying on the details of the
implementation they are using. Therefore macros defined via syntax-match
(and that includes macros defined via def-syntax) provide out of the box
introspection and debugging features.
Of course, readers who want to rely on the debugging tools of their
implementation can do so; for instance I hear that DrScheme has a
pretty good macro stepper but I have not tried it since I am an
Emacs-addict.
First of all, you should download and install the right sweet-macros library.
Unfortunately the R6RS module system does not really solve the portability
issue (to my endless frustration) so I had to write different versions
of the same library :-( I you are using Ikarus you should download the
single file version of the library
and put it everywhere in you IKARUS_LIBRARY_PATH. If you are using PLT
Scheme (you need a version of PLT newer than 4.0 for R6RS support) you
must download the zip file version
and install it in your collects directory, which on my machine is
$HOME/.plt-scheme/4.0/collects.
Actually, the multifile version of the library works also with Ikarus
if you have a recent enough version (right now I am using the trunk,
version 0.0.3+, revision 1654). I have not tried the library on Larceny;
I have tried it in Ypsilon Scheme which however has a small bug
so that it does not run there (the bug is already fixed in the trunk).
You should always keep in mind than R6RS
implementations are pretty young and that implementors are still working
to make them really compatible.
I have also prepared an R5RS version which should work in Chicken Scheme,
at least in the interpreter:
However I have developed and tested sweet-macros in Ikarus only
(caveat emptor!). Still, since the title of this blog is The Explorer,
I think it is fine if we deal with exploratory code.
You can check that the installation went well by importing the
library:
As you see, Scheme macros are based on pattern matching:
we are giving instructions to the compiler, specifying
how it must acts when it sees certain patterns. In our example,
when the compiler sees a multi-define expression followed by two
sequences with zero o more arguments, it must replace it
with a begin expression containing a sequence of zero or more definitions.
You can check that this is exactly what happens
by means of syntax-expand:
> (syntax-expand (multi-define (a b) (1 2)))
(begin (define a 1) (define b 2))
Notice that (multi-define()()) is valid code expanding to a do-nothing
(begin) expression; if you want to reject this corner case, you should
write your macro as
so that multi-define requires one or more arguments. However, it is
often useful to accept degenerate corner cases, because they may simplify
automatic code generation (i.e. multi-define could appear in the expansion
of another macro).
multi-define works as you would expect:
> (multi-define (a b) (1 2)) ; introduce the bindings a=1 and b=2
> a
1
> b
2
I have just scratched the surface of Scheme macros here: I leave the
rest for the next episode, don't miss it!
Not many comments yet ... How is it going, folks? Do you still think the pace is too slow (yes, I am referring to you, Merriodoc! ;)? The episodes you are reading now are somewhat of an experiment in teaching macrology: I am presenting Scheme macros the way *I* think is most pedagogic, but I may be wrong, so some feedback from people who never had previous exposure to Scheme macrology would be welcome. What would you like to see? More examples, more motivation, more code, less code, who knows? There are two more episodes on macros, and then a detour about functional programming in Scheme. Stay tuned!
; (syntax-expand (multi-define (a b) (1 2))) ; C:\Documents and Settings\grettke\Application Data\PLT Scheme\4.1.1\collects\sweet-macros\helper1.sls:56:11: compile: bad syntax; reference to top-level identifier is not allowed, because no #%top syntax transformer is bound in: syntax-match
> Not many comments yet ... > How is it going, folks?
I only starting reading your series early this morning :).
> Do you still think the pace is too > slow (yes, I am referring to you, Merriodoc! ;)?
I wish you hadn't skipped syntax-rules. It is easy to make the argument to go from syntax-rules to syntax-case:
1. You need error checking and reporting. 2. You need to break hygience.
Once you get syntax-rules, syntax-case isn't so hard. I will trust you claim that sweet-macros will make learning syntax-case easier.
> The episodes you are reading now are somewhat of an > experiment in teaching macrology: I am presenting Scheme > macros the way *I* think is most pedagogic, but I may be > wrong, so some feedback from people who never had previous > exposure to Scheme macrology would be welcome.
That is not me.
> What would you like to see? More examples, more > motivation, more code, less code, who knows? There are two > more episodes on macros, and then a detour about > functional programming in Scheme.
> Your directions work for PLT Scheme. Though, syntax-expand > isn't happy
I can't say I am surprised. We need some PLT expert to step on and explain what's happening. sweet-macros are stretching the limits of R6RS portability. They also are quite sensitive to bugs in syntax-case implementations, but in this case I think it is yet another issue related to phase separation.
By now you should have realized that syntax-rules macros are a special case of sweet-macros (syntactically you get syntax-rules macros by removing the #' characters from a sweet macro) so by teaching sweet-macros I am also teaching syntax-rules (and much more).
> > Your directions work for PLT Scheme. Though, > syntax-expand > > isn't happy > > I can't say I am surprised. We need some PLT expert to > step on and explain what's happening. sweet-macros are > stretching the limits of R6RS portability. They also are > quite sensitive to bugs in syntax-case implementations, > but in this case I think it is yet another issue related > to phase separation.
It was, and Abdul Aziz Ghuloum figured out how to fix it. If you download sweet-macros.zip again it should work now.