TODO: Maybe move the explanations to remarks and add examples separately
In Common Lisp, there is a concept of Generalized References. They allow a programmer to setf values to various "places" as if they were variables. Macros that make use of this ability often have a
F-postfix in the name. The place is usually the first argument to the macro.
A silly example, a macro that flips the sign of a number store in a place:
(defmacro flipf (place) `(setf ,place (- ,place)))
Macros that acquire and safely release a resource are usually named with a
WITH--prefix. The macro should usually use syntax like:
(with-foo (variable details-of-the-foo...) body...)
One approach to implementing this type of macro that can avoid some of the pitfalls of name pollution and unintended multiple evaluation is by implementing a functional version first. For instance, the first step in implementing a
with-widget macro that safely creates a widget and cleans up afterward might be a function:
(defun call-with-widget (args function) (let ((widget (apply #'make-widget args))) ; obtain WIDGET (unwind-protect (funcall function widget) ; call FUNCTION with WIDGET (cleanup widget) ; cleanup
Because this is a function, there are no concerns about the scope of names within function or supplier, and it makes it easy to write a corresponding macro:
(defmacro with-widget ((var &rest args) &body body) `(call-with-widget (list ,@args) (lambda (,var) ,@body)))
Macros that iterate over something are often named with a
DO-prefix. The macro syntax should usually be in form
(do-foo (variable the-foo-being-done return-value) body...)
Macros that match an input against certain cases are often named with a
CASE-postfix. There is often a
E...CASE-variant, which signals an error if the input doesn't match any of the cases, and
C...CASE, which signals a continuable error. They should have syntax like
(foocase input (case-to-match-against (optionally-some-params-for-the-case) case-body-forms...) more-cases... [(otherwise otherwise-body)])
For example, a macro that matches a string against regular expressions and binds the register groups to variables. Uses CL-PPCRE for regular expressions.
(defmacro regexcase (input &body cases) (let ((block-sym (gensym "block")) (input-sym (gensym "input"))) `(let ((,input-sym ,input)) (block ,block-sym ,@(loop for (regex vars . body) in cases if (eql regex 'otherwise) collect `(return-from ,block-sym (progn ,vars ,@body)) else collect `(cl-ppcre:register-groups-bind ,vars (,regex ,input-sym) (return-from ,block-sym (progn ,@body)))))))) (defun test (input) (regexcase input ("(\\d+)-(\\d+)" (foo bar) (format t "Foo: ~a, Bar: ~a~%" foo bar)) ("Foo: (\\w+)$" (foo) (format t "Foo: ~a.~%" foo)) (otherwise (format t "Didn't match.~%")))) (test "asd 23-234 qwe") ; Foo: 23, Bar: 234 (test "Foo: Foobar") ; Foo: Foobar. (test "Foo: 43 - 23") ; Didn't match.
Macros that define things are usually named either with