Macros by Example
What is it?
Macros by Example is a different style of macro writing for lisp, based on pattern/template pairs, rather than arbitrary procedures.
How do I use it?
A few examples should suffice to get you started
Suppose you want to write a macro similar to Common Lisp’s
increments a variable (by an optional amount), you can write
(mbe-defrules incf ((var) (setq var (+ var 1))) ((var by) (setq var (+ var by))))
The macro should be fairly self-explanatory. If you use the macro with
(incf foo), you will get the output
foo (+ foo 1)). If you use it with two,
(incf bar 42), you get the
(setq bar (+ bar 42)).
Similarly, you might try and write Elisp’s
push macro, like so
(mbe-defrules push ((newelt place) (setq place (cons newelt place))))
but you can also write it more tersely as
(mbe-defrule push (newelt place) (setq place (cons newelt place)))
So far, everything we have done could be simply done with defmacro,
but here we introduce a new character
(mbe-defrule let (((var val) ...) body ...) (funcall (lambda (var ...) body ...) val ...))
What do all these ellipses mean? Basically, they mean “match the
preceding pattern zero or more times”. In the pattern of
the first ellipsis follows the pattern
(var val) and so matches zero
or more lists of length two. Similarly, the second set of ellipsis
matches zero or more forms.
The ellipsis in the output forms is an implied iteration, where the
form before the ellipsis is included as many times as is variables
were matched in the pattern. For example, if you have the simple
(a ...) matching the list
(1 2 3), and the template
((a 1) ...) you will get the output
((1 1) (2 1) (3 1)).
One thing to notice is that
val do not appear together in
the output, that is perfectly okay. If
((var val) ..) matches
((1 2) (3 4) (5 6)), then
(var ... val ...) will give you the list
(1 3 5 2 4 6).
Anaphora are a controversial subject among Lispers, with Schemers like
myself arguing against them, but here is how you would a write
so-called “anaphoric if” with
(mbe-defrule aif (test consequent alternative) (let ((it test)) (if it consequent alternative)))
The macro brings no new difficulties, but it is here for a reason. If
you use scheme, you will expect that
it variable to be renamed
automatically by the macro system. This does not happen with
mbe-defrule. If you do
(if 3 (* it it) 42), you will get
as if you wrote the obvious defmacro implementation.
How do I get it?
If you have either the marmalade or melpa repositories, you can install mbe with
M-x package-install mbe
Otherwise, you can do it the old fashioned way.
Clone the repository
git clone https://github.com/ijp/mbe.el
Put the directory on your load path
(add-to-list 'load-path "$DOWNLOAD_DIR/mbe.el")
What license is it?
GPL 3 or higher, like basically all Elisp code.
Why not just use defmacro?
For simple macros, writing macros as pattern/template pairs can be much clearer than writing procedure code to output the same.
Why not just use defmacro and pcase?
Macros by example has a nice feature that pcase doesn’t have: ellipsis patterns, but it isn’t just about pattern matching the input, as you also use ellipsis in the output.
I’m not writing a macro, can I still use mbe’s pattern matching?
Yes, you can use the
(mbe-bind (a b c ... d) (list 1 2 3 4 5 6) (list a b c d)) ;; (1 2 (3 4 5) 6)
This looks like Scheme
Yes, that’s the point. The idea of Macros by Example was originally
invented by Eugene Kohlbecker and Mitchell Wand for Scheme in 1984,
and is an essential ingredient of the modern Scheme macro systems
syntax-case. You can find their technical report
Are these macros hygienic?
No, this code only implements pattern matching and template substitution.
Will you implement hygienic macros for Elisp?
Maybe one day, but that is not part of the scope of this project.
Why did you implement this?
On the #emacs irc channel Nic Ferrier was asking for an implementation of let* in terms of let, since GNU Emacs implements it in the C source code. I didn’t provide him with one, but I got thinking about how much clearer it is to write in Scheme than Elisp. The rest, as they say, is history.