One of the most common macros that almost everyone keeps in their utilities file is with-gensyms. With-gensyms is a macro that binds a list of variables to gensyms. Thats it! All with-gensyms does it take a list of symbols and generates code which binds each of those symbols to a gensym. Although with-gensyms is simple, it removes a lot of boiler plate code. Here is a simple implementation of with-gensyms:
(defmacro with-gensyms (vars &body body)
<code>(let ,(loop for v in vars collect </code>(,v (gensym)))
,@body))
Looking at my implementation of accum, here is how one could simplify it by using with-gensyms. Pay attention to how much boiler plate is removed.
(defmacro accum (accfn &body body)
(with-gensyms (ghead gtail garg)
<code>(let* ((,ghead (list nil))
(,gtail ,ghead))
(macrolet ((,accfn (,garg)
</code>(setf ,',gtail
(setf (cdr ,',gtail)
(list ,,garg)))))
,@body
(cdr ,ghead)))))
By removing so much boiler plate, with-gensyms helps greatly reduce the cognitive load in certain cases. This will be important when I introduce once-only, the next macro I plan to talk about. There are also other variations of with-gensyms such as the one in Alexandria which makes it easier to have base names associated with the gensyms created.